class Solution:
    # 20221208
    # https://leetcode.cn/problems/determine-color-of-a-chessboard-square/
    def squareIsWhite(self, coordinates: str) -> bool:
        if (ord(coordinates[0])-96 + int(coordinates[1]))%2:
            return True
        else:
            return False

    # 20221208
    # https://leetcode.cn/problems/dKk3P7/
    def isAnagram(self, s: str, t: str) -> bool:
        try:
            alphabet = {}
            for i in s:
                if i in alphabet:
                    alphabet[i] += 1
                else:
                    alphabet[i] = 1
            for i in t:
                alphabet[i] -= 1
            for i in alphabet:
                if alphabet[i] != 0:
                    return False

            if s != t:
                return True
            else:
                return False
        except:
            return False

    # 20221209
    # https://leetcode.cn/problems/check-if-number-is-a-sum-of-powers-of-three/
    def checkPowersOfThree(self, n: int) -> bool:
        while(n):
            if n%3 == 2:
                return False
            n //= 3
        return True
    
    # 20221216
    # https://leetcode.cn/problems/minimum-elements-to-add-to-form-a-given-sum/
    def minElements(self, nums: list[int], limit: int, goal: int) -> int:
        s = sum(nums)
        if abs(goal-s)%abs(limit) != 0:
            return abs(goal-s)//abs(limit)+1
        else:
            return abs(goal-s)//abs(limit)

    # 20221216
    # https://leetcode.cn/problems/largest-multiple-of-three/
    def largestMultipleOfThree(self, digits: list[int]) -> str:
        def largestMultipleOfThree_DeleteElement(m):
            for i in digits:
                if i%3 == m:
                    digits.remove(i)
                    return True
            return False

        digits.sort()
        s = sum(digits)
        if s%3 != 0:
            if not largestMultipleOfThree_DeleteElement(s%3):
                largestMultipleOfThree_DeleteElement(3-s%3)
                largestMultipleOfThree_DeleteElement(3-s%3)

        s = sum(digits)
        if len(digits) == 0:
            return ""
        else:
            if s == 0:
                return "0"
            else:
                return "".join(str(i) for i in digits[::-1])

    # 20221217
    # https://leetcode.cn/problems/form-array-by-concatenating-subarrays-of-another-array/
    def canChoose(self, groups: list[list[int]], nums: list[int]) -> bool:
        index = 0
        for l in groups:
            while l != nums[index:index+len(l)]:
                index += 1
                if index > len(nums):
                    return False
            index += len(l)
        return True
    
    # 20221217
    # https://leetcode.cn/problems/minimum-size-subarray-sum/
    def minSubArrayLen(self, target: int, nums: list[int]) -> int:
        left = 0
        right = 0
        sumt = 0
        res = 9999999999999
        for right in range(len(nums)):
            sumt += nums[right]
            while sumt >= target:
                res = min(right-left+1, res)
                sumt -= nums[left]
                left += 1

        if res == 9999999999999:
            return 0
        else:
            return res
    
    # 20221218
    # https://leetcode.cn/problems/minimum-adjacent-swaps-for-k-consecutive-ones/
    def minMoves(self, nums: list[int], k: int) -> int:
        # 数字1的位置数组
        arr = []
        for i in range(len(nums)):
            if nums[i]:
                arr.append(i)
        
        # 数字1的中位数和，计算k个1移动到一起需要的距离，即交换次数，滑动窗口初始值
        res = 0
        for i in range(k):
            res += abs(arr[i]-arr[k//2]) - abs(i-k//2)

        cur = res
        for i in range(k, len(arr)):
            # 更新中位数
            mid = i-k+k//2
            # pop最左边元素
            cur -= arr[mid]-arr[i-k]-(mid-(i-k)) 
            # 中位数右移
            mid = i-(k-1)+(k-1)//2
            # append最右边元素
            cur += arr[i]-arr[mid]-(i-mid)
            
            res=min(res,cur)
        
        return res 

    # 20221218
    # https://leetcode.cn/problems/shortest-supersequence-lcci/
    def shortestSeq(self, big: list[int], small: list[int]) -> list[int]:
        res = [0, 100001]
        l = r =  0
        dic = {}
        diff = len(small)
        for i in small:
            dic[i] = 1 
        
        while r < len(big):
            if big[r] in dic:
                dic[big[r]] -= 1
                if dic[big[r]] == 0:
                    diff -= 1

            while diff == 0:
                if res[1]-res[0] > r-l:
                    res[0] = l
                    res[1] = r
                if big[l] in dic:
                    dic[big[l]] += 1
                    if dic[big[l]] > 0:
                        diff += 1
                l += 1
            r += 1

        if res == [0, 100001]:
            return []
        else:
            return res

    # 20221218
    # https://leetcode.cn/problems/nGK0Fy/
    def calculate(self, s: str) -> int:
        x = 1
        y = 0
        for i in s:
            if i == "A":
                x = 2*x+y
            else:
                y = 2*y+x
        return x+y
    
    # 20221219
    # https://leetcode.cn/problems/find-if-path-exists-in-graph/
    # https://leetcode.cn/problems/find-if-path-exists-in-graph/solution/python-bing-cha-ji-huan-neng-zai-luo-yi-wuyr2/
    def validPath(self, n: int, edges: List[List[int]], source: int, destination: int) -> bool:
        p = [i for i in range(n)]
        
        def get_root(node):
            if p[node] == node:
                return p[node]
            p[node] = get_root(p[node])
            return p[node]

        def merge(a, b):
            root_a, root_b = get_root(a), get_root(b)
            if root_a != root_b:
                p[root_a] = root_b

        for a, b in edges:
            merge(a, b)

        return get_root(source) == get_root(destination)

    # 20221220
    # https://leetcode.cn/problems/minimum-limit-of-balls-in-a-bag/
    # https://leetcode.cn/problems/minimum-limit-of-balls-in-a-bag/solution/ji-jian-er-fen-cha-zhao-by-luo-bi-da-qua-q4nx/
    def minimumSize(self, nums: List[int], maxOperations: int) -> int:
        l, r = 1, max(nums)
        while l < r:
            mid = (l + r) // 2
            op = sum((x-1) // mid for x in nums)   
            if op > maxOperations:
                l = mid + 1
            else:
                r = mid  
        return l
    
    # 20221221
    # https://leetcode.cn/problems/maximum-score-from-removing-stones/
    def maximumScore(self, a: int, b: int, c: int) -> int:
        arr = [a, b, c]
        arr.sort()
        if arr[0]+arr[1] <= arr[2]:
            return (arr[0]+arr[1])
        else:
            return (arr[0]+arr[1]+arr[2])//2

    # 20221222
    # https://leetcode.cn/problems/maximize-score-after-n-operations/
    # https://leetcode.cn/problems/maximize-score-after-n-operations/solution/by-lcbin-8uxm/
    def maxScore(self, nums: List[int]) -> int:
        m = len(nums)
        g = [[0] * m for _ in range(m)]
        for i in range(m):
            for j in range(i+1, m):
                g[i][j] = gcd(nums[i], nums[j])
        f = [0] * (1 << m)
        for k in range(1 << m):
            if (cnt := k.bit_count()) % 2 == 0:
                for i in range(m):
                    if k >> i & 1:
                        for j in range(i + 1, m):
                            if k >> j & 1:
                                f[k] = max(f[k], f[k ^ (1 << i) ^ (1 << j)] + cnt // 2 * g[i][j])
        return f[-1]

    # 20221223
    # https://leetcode.cn/problems/final-value-of-variable-after-performing-operations/
    def finalValueAfterOperations(self, operations: List[str]) -> int:
        x = 0
        for o in operations:
            if "+" in o:
                x += 1
            else:
                x += -1
        return x
    
    # 20221224
    # https://leetcode.cn/problems/largest-merge-of-two-strings/
    def largestMerge(self, word1: str, word2: str) -> str:
        if len(word1) == 0 or len(word2) == 0: 
            return word1 + word2
        if word1 >= word2: 
            return word1[0]+self.largestMerge(word1[1:],word2)
        else:
            return word2[0]+self.largestMerge(word1,word2[1:])
    
    # 20221225
    # https://leetcode.cn/problems/building-boxes/
    # https://leetcode.cn/problems/building-boxes/solution/mei-xiang-ming-bai-yi-ge-dong-hua-miao-d-8vbe/
    def minimumBoxes(self, n: int) -> int:
        ans = max_n = 0
        i = j = 1
        while max_n + ans + i <= n:
            ans += i
            max_n += ans
            i += 1
        while max_n < n:
            ans += 1
            max_n += j
            j += 1
        return ans

    # 20221226
    # https://leetcode.cn/problems/count-number-of-homogenous-substrings/
    # https://leetcode.cn/problems/count-number-of-homogenous-substrings/solution/bian-li-zhi-jie-xiang-jia-dang-qian-lian-irb9/
    def countHomogenous(self, s: str) -> int:
        pre = None
        mod = 10**9 + 7
        res = cnt = 0
        for ss in s:
            if ss == pre:
                cnt += 1
            else:
                pre = ss
                cnt = 1
            res += cnt
            res %= mod
        return res
    
    # 20221227
    # https://leetcode.cn/problems/minimum-moves-to-convert-string/
    def minimumMoves(self, s: str) -> int:
        ans = i = 0
        while i < len(s):
            if s[i] == "X":
                ans += 1
                i += 3
            else:
                i += 1
        return ans

    # 20221228
    # https://leetcode.cn/problems/minimum-length-of-string-after-deleting-similar-ends/
    # https://leetcode.cn/problems/minimum-length-of-string-after-deleting-similar-ends/solution/python-shuang-zhi-zhen-ti-jie-han-ti-mu-fayx4/
    def minimumLength(self, s: str) -> int:
        left = 0
        right = len(s)-1
        while left < right and s[left] == s[right]:
            c = s[left]
            while left <= right and s[left] == c:
                left += 1
            while left <= right and s[right] == c:
                right -= 1
        return right - left + 1

    # 20221229
    # https://leetcode.cn/problems/two-out-of-three/
    def twoOutOfThree(self, nums1: List[int], nums2: List[int], nums3: List[int]) -> List[int]:
        r = []
        for i in nums1:
            if i in nums2 or i in nums3:
                r.append(i)
        for i in nums2:
            if i in nums3:
                r.append(i)
        return list(set(r))

    # 20221231
    # https://leetcode.cn/problems/minimum-number-of-moves-to-seat-everyone/
    def minMovesToSeat(self, seats: List[int], students: List[int]) -> int:
        for n in seats:
            if n in students:
                seats.remove(n)
                students.remove(n)
        r = 0
        i = 0
        seats.sort()
        students.sort()
        while i < len(seats):
            r += abs(seats[i]-students[i])
            i += 1
        return r
    
    # 20221210
    # https://leetcode.cn/problems/maximum-height-by-stacking-cuboids/submissions/
    # https://leetcode.cn/problems/maximum-height-by-stacking-cuboids/solution/by-laughing-davincizbw-8ify/
    def maxHeight(self, cuboids: List[List[int]]) -> int:
        for i in cuboids:
            i.sort()
        dp = sorted(cuboids)
        for i, v in enumerate(dp):
            dp[i] = (v[2], v[1], v[2])
            for j in dp[:i]:
                if j[1] <= v[1] and j[2] <= v[2]:
                    if (s := j[0] + v[2]) > dp[i][0]:
                        dp[i] = (s, v[1], v[2])
        return max(dp)[0]

    # 20230101
    # https://leetcode.cn/problems/first-letter-to-appear-twice/
    def repeatedCharacter(self, s: str) -> str:
        for i in range(1, len(s)):
            if s[i] in s[0:i]:
                return s[i]

    # 20230102
    # https://leetcode.cn/problems/number-of-orders-in-the-backlog/
    # https://leetcode.cn/problems/number-of-orders-in-the-backlog/solution/python-dui-de-jian-dan-ying-yong-qing-xi-5hob/
    def getNumberOfBacklogOrders(self, orders: List[List[int]]) -> int:
        sell = []
        buy = []
        for order in orders:
            if order[2] == 0:
                #buy
                if not sell or sell[0][0] > order[0]:
                    heapq.heappush(buy, [-order[0], order[1]])
                else:
                    rem = order[1]
                    while rem > 0 and sell and sell[0][0] <= order[0]:
                        k, s = heapq.heappop(sell)
                        cancel = min(s, rem)
                        s -= cancel
                        rem -= cancel
                    if rem > 0:
                        heapq.heappush(buy, [-order[0], rem])
                    if s > 0:
                        heapq.heappush(sell, [k, s])
            if order[2] == 1:
                #sell
                if not buy or -buy[0][0] < order[0]:
                    heapq.heappush(sell, [order[0], order[1]])
                else:
                    rem = order[1]
                    while rem > 0 and buy and -buy[0][0] >= order[0]:
                        k, s = heapq.heappop(buy)
                        cancel = min(s, rem)
                        s -= cancel
                        rem -= cancel
                    if rem > 0:
                        heapq.heappush(sell, [order[0], rem])
                    if s > 0:
                        heapq.heappush(buy, [k, s])
        return (sum([a[1] for a in sell])+sum([a[1] for a in buy])) % (10**9+7)

    # 20230103
    # https://leetcode.cn/problems/check-if-numbers-are-ascending-in-a-sentence/
    def areNumbersAscending(self, s: str) -> bool:
        n = 0
        for w in s.split(' '):
            try:
                if int(w) > n:
                    n = int(w)
                else:
                    return False
            except:
                continue
        return True

    # 20230104
    # https://leetcode.cn/problems/maximum-value-at-a-given-index-in-a-bounded-array/
    # https://leetcode.cn/problems/maximum-value-at-a-given-index-in-a-bounded-array/solution/kou-xi-jie-by-rumif-znwf/
    def maxValue(self, n: int, index: int, maxSum: int) -> int:
        l = maxSum // n
        r = maxSum
        left = index
        right = n - 1 - index
        while l <= r:
            mid = l + ((r - l) >> 1)
            if mid - left > 0:
                sumLeft = ((mid - left + mid) * (left + 1)) >> 1
            else:
                sumLeft = (((1 + mid) * mid) >> 1) + left - mid + 1
            if mid - right > 0:
                sumRight = ((mid - right + mid) * (right + 1)) >> 1
            else:
                sumRight = (((1 + mid) * mid) >> 1) + right - mid + 1
            total = sumLeft + sumRight - mid
            if total > maxSum:
                r = mid - 1
            else:
                l = mid + 1
        return r

    # 20230105
    # https://leetcode.cn/problems/count-pairs-with-xor-in-a-range/
    # https://leetcode.cn/problems/count-pairs-with-xor-in-a-range/solution/by-musing-clarkeeae-9bee/
    def countPairs(self, nums: List[int], low: int, high: int) -> int:
        nums.sort()

        def f(k):
            ans = -len(nums)
            for x in nums:
                y = 0
                for i in range(14, -1, -1):
                    if k & 1 << i:
                        z = y + (x & 1 << i)
                        ans += bisect_right(nums, z + (1 << i) - 1) - bisect_right(nums, z - 1)
                    y += (k & 1 << i) ^ (x & 1 << i)
            return ans

        return (f(high + 1) - f(low)) >> 1

    # 20230106
    # https://leetcode.cn/problems/count-integers-with-even-digit-sum/
    def countEven(self, num: int) -> int:
        n = 1
        r = 0
        while n <= num:
            t = 0
            for i in str(n):
                t += int(i)
            if t % 2 == 0:
                r += 1
            n += 1
        return r

    # 20230107
    # https://leetcode.cn/problems/minimum-operations-to-reduce-x-to-zero/
    # https://leetcode.cn/problems/minimum-operations-to-reduce-x-to-zero/solution/python3hua-dong-chuang-kou-by-yslucas/
    def minOperations(self, nums: List[int], x: int) -> int:
        res = -1
        target = sum(nums) - x
        left = right = 0
        n = len(nums)
        cur = 0
        while left < n and right < n:
            if right < n:
                cur += nums[right]
                right += 1
            while cur > target and left < n:
                cur -= nums[left]
                left += 1
            if cur == target:
                res = max(res, right - left)
        return n - res if res >= 0 else -1

    # 20230108
    # https://leetcode.cn/problems/counting-words-with-a-given-prefix/
    def prefixCount(self, words: List[str], pref: str) -> int:
        r = 0
        for i in words:
             if pref == i[:len(pref)]:
                 r += 1
        return r

    # 20230109
    # https://leetcode.cn/problems/minimum-number-of-operations-to-reinitialize-a-permutation/
    def reinitializePermutation(self, n: int) -> int:
        perm = list(range(n))
        init = perm.copy()
        step = 0
        while True:
            step += 1
            temp = list(range(n))
            for i in range(n):
                if i%2:
                    temp[i]=perm[n//2+(i-1)//2]
                else:
                    temp[i]=perm[i//2]
            perm=temp
            if perm == init:
                return step

    # 20230110
    # https://leetcode.cn/problems/cracking-the-safe/
    # https://leetcode.cn/problems/cracking-the-safe/solution/py3-quan-pai-lie-by-wzhaooooo/
    def crackSafe(self, n: int, k: int) -> str:
        used = []
        ans = '0' * n
        used.append(ans)
        for i in range(1,k ** n):
            temp = ans[len(ans) - n + 1:len(ans)]
            for j in range(k - 1,-1,-1):
                new_s = temp + str(j)
                if new_s not in used:
                    used.append(new_s)
                    ans += str(j)
                    break
        return ans

    # 20230111
    # https://leetcode.cn/problems/check-if-number-has-equal-digit-count-and-digit-value/
    def digitCount(self, num: str) -> bool:
        for i in range(len(num)):
            if not num.count(str(i)) == int(num[i]):
                return False
        return True

    # 20230112
    # https://leetcode.cn/problems/evaluate-the-bracket-pairs-of-a-string/
    def evaluate(self, s: str, knowledge: List[List[str]]) -> str:
        k = {}
        for i in knowledge:
            k[i[0]] = i[1]
        sl = re.split('\(|\)', s)
        for i in range(1, len(sl), 2):
            if sl[i] in k:
                sl[i] = k[sl[i]]
            else:
                sl[i] = '?'
        return ''.join(sl)

    # 20230113
    # https://leetcode.cn/problems/rearrange-characters-to-make-target-string/
    def rearrangeCharacters(self, s: str, target: str) -> int:
        r = 100
        for c in target:
            r = min(r, s.count(c)//target.count(c))
        return r

    # 2023114
    # https://leetcode.cn/problems/number-of-different-subsequences-gcds/
    # https://leetcode.cn/problems/number-of-different-subsequences-gcds/solution/by-lcbin-c0cq/
    def countDifferentSubsequenceGCDs(self, nums: List[int]) -> int:
        mx = max(nums)
        nums = set(nums)
        ans = 0
        for x in range(1, mx+1):
            g = 0
            for y in range(x, mx+1, x):
                if y in nums:
                    g = gcd(g, y)
                    if g == x:
                        ans += 1
                        break
        return ans

    # 20230115
    # https://leetcode.cn/problems/min-max-game/
    def minMaxGame(self, nums: List[int]) -> int:
        n = len(nums)
        newNums = [0] * (int(n/2))
        while (n > 1):
            for i in range(int(n/2)):
                if i%2:
                    newNums[i] = max(nums[2*i], nums[2*i+1])
                else:
                    newNums[i] = min(nums[2*i], nums[2*i+1])
            nums = newNums
            n = len(nums)
            newNums = [0] * (int(n/2))
        return nums[0]

    # 20230116
    # https://leetcode.cn/problems/sentence-similarity-iii/
    def areSentencesSimilar(self, sentence1: str, sentence2: str) -> bool:
        s1 = sentence1.split(' ')
        s2 = sentence2.split(' ')
        if len(s1) < len(s2):
            sa1, sa2 = s1, s2
        else:
            sa1, sa2 = s2, s1

        i, j, k = 0, len(sa1)-1, len(sa2)-1
        while i <= j:
            if sa1[i] == sa2[i]:
                i += 1
            elif sa1[j] == sa2[k]:
                j -= 1
                k -= 1
            else:
                break
        if i == j+1:
            return True
        else: 
            return False

    # 20230117
    # https://leetcode.cn/problems/count-nice-pairs-in-an-array/
    # https://leetcode.cn/problems/count-nice-pairs-in-an-array/solution/1814-ha-xi-ji-shu-by-desti-cx8x/
    def countNicePairs(self, nums: List[int]) -> int:
        res = 0
        k = {}
        for i in range(len(nums)):
            n = nums[i] - int(str(nums[i])[::-1])
            if n in k:
                k[n] += 1
            else:
                k[n] = 1
        for i in k:
            res += int(k[i]*(k[i]-1)/2)
        return res % 1000000007

    # 202301119
    # https://leetcode.cn/problems/strong-password-checker-ii/
    def strongPasswordCheckerII(self, password: str) -> bool:
        if len(password) < 8:
            return False
        else:
            lowflag = False
            highflag = False
            numflag = False
            extraflag = False
            lastchar = ""
            for i in password:
                if i.islower():
                    lowflag = True
                elif i.isupper():
                    highflag = True
                elif i.isdigit():
                    numflag = True
                else:
                    extraflag = True
                
                if i == lastchar:
                    return False
                else:
                    lastchar = i

        return lowflag&highflag&numflag&extraflag
    
    # 20230120
    # https://leetcode.cn/problems/finding-the-users-active-minutes/
    def findingUsersActiveMinutes(self, logs: List[List[int]], k: int) -> List[int]:
        d = {}
        for i in logs:
            if i[0] not in d:
                d[i[0]] = {i[1]}
            else:
                d[i[0]].add(i[1])

        res = [0] * k
        value = list(d.values())
        for i in value:
            res[len(i)-1] += 1
        return res

    # 20230121
    # https://leetcode.cn/problems/minimum-sideway-jumps/
    # https://leetcode.cn/problems/minimum-sideway-jumps/solution/cong-0-dao-1-de-0-1-bfspythonjavacgo-by-1m8z4/
    def minSideJumps(self, obstacles: List[int]) -> int:
        n = len(obstacles)
        dis = [[n] * 3 for _ in range(n)]
        dis[0][1] = 0
        q = deque([(0, 1)])  # 起点
        while True:
            i, j = q.popleft()
            d = dis[i][j]
            if i == n - 1: return d  # 到达终点
            if obstacles[i + 1] != j + 1 and d < dis[i + 1][j]:  # 向右
                dis[i + 1][j] = d
                q.appendleft((i + 1, j))  # 加到队首
            for k in (j + 1) % 3, (j + 2) % 3:  # 枚举另外两条跑道（向上/向下）
                if obstacles[i] != k + 1 and d + 1 < dis[i][k]:
                    dis[i][k] = d + 1
                    q.append((i, k))  # 加到队尾

    # 20230122
    # https://leetcode.cn/problems/maximum-number-of-groups-getting-fresh-donuts/
    # https://leetcode.cn/problems/maximum-number-of-groups-getting-fresh-donuts/solution/by-lcbin-wtvs/
    def maxHappyGroups(self, batchSize: int, groups: List[int]) -> int:
        @cache
        def dfs(state, mod):
            res = 0
            x = int(mod == 0)
            for i in range(1, batchSize):
                if state >> (i * 5) & 31:
                    t = dfs(state - (1 << (i * 5)), (mod + i) % batchSize)
                    res = max(res, t + x)
            return res

        state = ans = 0
        for v in groups:
            i = v % batchSize
            ans += i == 0
            if i:
                state += 1 << (i * 5)
        ans += dfs(state, 0)
        return ans
    
    # 20230123
    # https://leetcode.cn/problems/calculate-amount-paid-in-taxes/
    def calculateTax(self, brackets: List[List[int]], income: int) -> float:
        ans = 0
        low = 0
        for i in brackets:
            if income > i[0]:
                ans += i[1]*(i[0]-low)/100
                low = i[0]
            else:
                ans += i[1]*(income-low)/100
                break
        return ans

    # 20230124
    # https://leetcode.cn/problems/queries-on-number-of-points-inside-a-circle/
    def countPoints(self, points: List[List[int]], queries: List[List[int]]) -> List[int]:
        ans = []
        for x, y, r in queries:
            cnt = 0
            for i, j in points:
                dx, dy = i - x, j - y
                if dx*dx + dy*dy <= r*r:
                    cnt += 1
            ans.append(cnt)
        return ans
        
    # 20230125
    # https://leetcode.cn/problems/rank-transform-of-a-matrix/
    # https://leetcode.cn/problems/rank-transform-of-a-matrix/solution/python3-mei-sha-ji-zhu-han-liang-de-ti-jie-by-simp/
    def matrixRankTransform(self, matrix: List[List[int]]) -> List[List[int]]:
        LIM = 512
        R, C = len(matrix), len(matrix[0])
        res = [[0]*C for _ in range(R)]
        countR, countC = [0]*R, [0]*C
        
        # 按元素大小分别存储元素坐标
        ls = collections.defaultdict(list)
        for r, row in enumerate(matrix): 
            for c, val in enumerate(row): 
                ls[val].append((r, c))
                
        # 并查集用于合并行或列相同的元素
        union = list(range(LIM*2))
        def find(i): 
            if union[i] == i: return i
            union[i] = find(union[i])
            return union[i]
        
        # 按val从小到大遍历
        pool = collections.defaultdict(list)
        for val in sorted(ls.keys()): 

            # 用并查集合并行和列相同的元素并分组
            for r, c in ls[val]: 
                union[find(r)] = find(c+LIM)
            pool.clear()
            for r, c in ls[val]: 
                pool[find(r)].append((r, c))

                
            # 行和列相同的元素，共享相同的rank
            for group in pool.values(): 
                rank = max(max((countR[r], countC[c])) for r, c in group) + 1
                for r, c in group: 
                    countR[r] = countC[c] = res[r][c] = rank
                    # 重置并查集
                    union[r] = r
                    union[c+LIM] = c+LIM
        return res

    # 20230126
    # https://leetcode.cn/problems/smallest-string-with-a-given-numeric-value/
    # https://leetcode.cn/problems/smallest-string-with-a-given-numeric-value/solution/by-lcbin-7102/
    def getSmallestString(self, n: int, k: int) -> str:
        ans = ['a'] * n
        d = k - n
        i = n - 1
        while d > 25:
            ans[i] = 'z'
            d -= 25
            i -= 1
        ans[i] = chr(ord(ans[i]) + d)
        return ''.join(ans)

    # 20230127
    # https://leetcode.cn/problems/greatest-english-letter-in-upper-and-lower-case/
    def greatestLetter(self, s: str) -> str:
        ans = ""
        for word in s:
            if chr(ord(word)+32) in s:
                if chr(ord(word)+32) > ans:
                    ans = chr(ord(word)+32)
        return ans.upper()

    # 20230128
    # https://leetcode.cn/problems/ways-to-make-a-fair-array/
    # https://leetcode.cn/problems/ways-to-make-a-fair-array/solution/1664-sheng-cheng-ping-heng-shu-zu-de-fan-4bbl/
    def waysToMakeFair(self, nums: List[int]) -> int:
        left_1, left_0, right_1, right_0, ans  = 0, 0, sum(nums[::2]), sum(nums[1::2]), 0

        for i in range(len(nums)):
            if i % 2: 
                right_0 -= nums[i]
                if left_1 + right_0 == left_0 + right_1:
                    ans += 1
                left_0 += nums[i]
            else:
                right_1 -= nums[i]
                if left_1 + right_0 == left_0 + right_1:
                    ans += 1
                left_1 += nums[i]

        return ans

    # 20230129
    # https://leetcode.cn/problems/count-asterisks/
    def countAsterisks(self, s: str) -> int:
        flag = True
        ans = 0
        for i in s:
            if i == "|" and flag:
                flag = False
                continue
            if i == "|" and not flag:
                flag = True
            if i == "*" and flag:
                ans += 1
        return ans

    # 20230130
    # https://leetcode.cn/problems/merge-in-between-linked-lists/
    # https://leetcode.cn/problems/merge-in-between-linked-lists/solution/1669-he-bing-liang-ge-lian-biao-lian-bia-9opg/
    def mergeInBetween(self, list1: ListNode, a: int, b: int, list2: ListNode) -> ListNode:
        pa = list1
        for _ in range(a - 1): pa = pa.next
        pb = pa.next
        for _ in range(b - a): pb = pb.next
        pa.next = list2
        while pa.next: 
            pa = pa.next
        pa.next = pb.next
        return list1

    # 20230131
    # https://leetcode.cn/problems/check-if-matrix-is-x-matrix/
    def checkXMatrix(self, grid: List[List[int]]) -> bool:
        for i in range(len(grid)):
            for j in range(len(grid[i])):
                if i == j or i == len(grid[i])-j-1:
                    if not grid[i][j]:
                        return False 
                else:
                    if grid[i][j]:
                        return False 
        return True

    # 20230201
    # https://leetcode.cn/problems/decode-the-message/submissions/
    def decodeMessage(self, key: str, message: str) -> str:
        nl = list({}.fromkeys(key).keys())
        if " " in nl:
            nl.remove(" ")
        ans = ''
        for i in message:
            if i == " ":
                ans += " "
            else:
                ans += chr(97+nl.index(i))
        return ans

    # 20230202
    # https://leetcode.cn/problems/shortest-path-with-alternating-colors/
    # https://leetcode.cn/problems/shortest-path-with-alternating-colors/solution/tu-jie-hong-lan-liang-lu-jiao-cha-bfs-by-coldme-2/
    def shortestAlternatingPaths(self, n: int, red_edges: List[List[int]], blue_edges: List[List[int]]) -> List[int]:
        red_path = [set() for i in range(n)]
        blue_path = [set() for i in range(n)]
        dist = [[None, None] for i in range(n)]
        dist[0] = [0, 0]
        step = 0
        now_red = [0]
        now_blue = [0]
        for start, end in red_edges:
            red_path[start].add(end)
        for start, end in blue_edges:
            blue_path[start].add(end)
        # step 1 找到分别以红边开始和以蓝边开始的两条最短路径
        while len(now_red) != 0 or len(now_blue) != 0 :
            new_red, new_blue = [], []
            step += 1
            if len(now_blue) != 0:
                for point in now_blue:
                    for next_point in red_path[point]:
                        if dist[next_point][0] is None:
                            new_red.append(next_point)
                            dist[next_point][0] = step
            if len(now_red) != 0:
                for point in now_red:
                    for next_point in blue_path[point]:
                        if dist[next_point][1] is None:
                            new_blue.append(next_point)
                            dist[next_point][1] = step
            now_red, now_blue = new_red, new_blue
        # step 2 在这两条最短路径中选择小的，merge成我们的答案
        ans = []
        for i in range(n):
            if dist[i][0] is None and dist[i][1] is None:
                ans.append(-1)
            elif dist[i][0] is not None and dist[i][1] is not None:
                ans.append(min(dist[i][0], dist[i][1]))
            elif dist[i][0] is not None:
                ans.append(dist[i][0])
            else:
                ans.append(dist[i][1])
        return ans

    # 20230203
    # https://leetcode.cn/problems/binary-tree-coloring-game/
    # https://leetcode.cn/problems/binary-tree-coloring-game/solution/mei-you-si-lu-yi-zhang-tu-miao-dong-pyth-btav/
    def btreeGameWinningMove(self, root: Optional[TreeNode], n: int, x: int) -> bool:
        lsz = rsz = 0
        def dfs(node: Optional[TreeNode]) -> int:
            if node is None:
                return 0
            ls = dfs(node.left)
            rs = dfs(node.right)
            if node.val == x:
                nonlocal lsz, rsz
                lsz, rsz = ls, rs
            return ls + rs + 1
        dfs(root)
        return max(lsz, rsz, n - 1 - lsz - rsz) * 2 > n

    # 20230204
    # https://leetcode.cn/problems/maximum-number-of-consecutive-values-you-can-make/
    # https://leetcode.cn/problems/maximum-number-of-consecutive-values-you-can-make/solution/ni-neng-gou-zao-chu-lian-xu-zhi-de-zui-d-hlxf/
    def getMaximumConsecutive(self, coins: List[int]) -> int:
        coins.sort()
        x = 0
        for y in coins:
            if y > x + 1:
                break
            x += y
        return x + 1

    # 20230205
    # https://leetcode.cn/problems/minimum-moves-to-reach-target-with-rotations/
    # https://leetcode.cn/problems/minimum-moves-to-reach-target-with-rotations/solution/huan-zai-if-elseyi-ge-xun-huan-chu-li-li-tw8b/
    def minimumMoves(self, g: List[List[int]]) -> int:
        step, n = 1, len(g)
        vis = {(0, 0, 0)}
        q = [(0, 0, 0)]  # 初始位置
        while q:
            tmp = q
            q = []
            for X, Y, S in tmp:
                for t in (X + 1, Y, S), (X, Y + 1, S), (X, Y, S ^ 1):  # 直接把移动后的位置算出来
                    x, y, s = t
                    x2, y2 = x + s, y + (s ^ 1)  # 蛇头
                    if x2 < n and y2 < n and t not in vis and \
                       g[x][y] == 0 and g[x2][y2] == 0 and (s == S or g[x + 1][y + 1] == 0):
                        if x == n - 1 and y == n - 2:  # 此时蛇头一定在 (n-1,n-1)
                            return step
                        vis.add(t)
                        q.append(t)
            step += 1
        return -1

    # 20230206
    # https://leetcode.cn/problems/evaluate-boolean-binary-tree/
    def evaluateTree(self, root: Optional[TreeNode]) -> bool:
        if root.val == 0:
            return False
        elif root.val == 1:
            return True
        elif root.val == 2:
            return self.evaluateTree(root.left) or self.evaluateTree(root.right)
        elif root.val == 3:
            return self.evaluateTree(root.left) and self.evaluateTree(root.right)
        
    # 20230207
    # https://leetcode.cn/problems/alert-using-same-key-card-three-or-more-times-in-a-one-hour-period/
    def alertNames(self, keyName: List[str], keyTime: List[str]) -> List[str]:
        db = {}
        for i in range(len(keyName)):
            if keyName[i] in db.keys():
                db[keyName[i]].append(keyTime[i])
            else:
                db[keyName[i]] = [keyTime[i]]
        res = []
        for name in db.keys():
            time = sorted(db[name])
            if len(time) < 3:
                continue
            else:
                for i in range(len(time)-2):
                    endt = int(time[i+2].split(":")[0])*60+int(time[i+2].split(":")[1])
                    start = int(time[i].split(":")[0])*60+int(time[i].split(":")[1])
                    if (endt - start <= 60):
                        res.append(name)
                        break
        return sorted(res)

    # 20230208
    # https://leetcode.cn/problems/remove-sub-folders-from-the-filesystem/
    def removeSubfolders(self, folder: List[str]) -> List[str]:
        folder.sort()   
        res = [folder[0]]     
        for name in folder:
            if not res[-1]+"/" == name[:len(res[-1])+1]:
                res.append(name)
        return res[1:]
            
    # 20230210
    # https://leetcode.cn/problems/dice-roll-simulation/
    # https://leetcode.cn/problems/dice-roll-simulation/solution/dong-tai-gui-hua-by-shan-zhai-xiao-yao-z-ngg3/        
    def dieSimulator(self, n: int, rollMax: List[int]) -> int:
        MOD = 10**9+7
        # dp[i][j] 表示i次掷色子，结果为j的所有序列的个数
        dp = [[0]*6 for i in range(n)]
        for i in range(6):
            dp[0][i] = 1
        # dp[i][j] = \Sigma_{k}dp[i-1][k] - \Sigma_{k!=j}dp[i-rollMax[j]-1][k]
        for i  in range(1,n):
            for j in range(6):
                dp[i][j] = sum(dp[i-1])
                if i > rollMax[j]:
                    dp[i][j] = (dp[i][j]-(sum(dp[i-rollMax[j]-1])-dp[i-rollMax[j]-1][j]))%MOD
                elif i == rollMax[j]:
                    dp[i][j] = (dp[i][j]-dp[i-rollMax[j]][j])%MOD
        return sum(dp[n-1])%MOD

    # 20230211
    # https://leetcode.cn/problems/minimum-amount-of-time-to-fill-cups/
    def fillCups(self, amount: List[int]) -> int:
        amount.sort()
        if amount[0] + amount[1] <= amount[2]:
            return amount[2]
        else:
            ans = amount[0]
            while amount[0] > 0:
                if amount[1] <= amount[2]:
                    amount[2] -= 1
                else:
                    amount[1] -= 1
                amount[0] -= 1
            ans += max(amount[1], amount[2])
            return ans

    # 20230212
    # https://leetcode.cn/problems/alphabet-board-path/
    def alphabetBoardPath(self, target: str) -> str:
        board = ["abcde", "fghij", "klmno", "pqrst", "uvwxy", "z"]
        x, y = 0, 0
        ans = ""
        for t in target:
            for tsy in board:
                if t in tsy:
                    ty = board.index(tsy)
                    for tsx in tsy:
                        if tsx == t:
                            tx = tsy.index(tsx)
                            if tx - x < 0:
                                ans += "L"*(x-tx)
                            if ty - y < 0:
                                ans += "U"*(y-ty)
                            if tx - x > 0:
                                ans += "R"*(tx-x)
                            if ty - y > 0:
                                ans += "D"*(ty-y)
                            ans += "!"
                            x = tx
                            y = ty
        return ans
    
    # 20230213
    # https://leetcode.cn/problems/replace-the-substring-for-balanced-string/
    # https://leetcode.cn/problems/replace-the-substring-for-balanced-string/solution/tong-xiang-shuang-zhi-zhen-hua-dong-chua-z7tu/
    def balancedString(self, s: str) -> int:
        cnt, m = Counter(s), len(s) // 4
        if all(cnt[x] == m for x in "QWER"):  # 已经符合要求啦
            return 0
        ans, left = inf, 0
        for right, c in enumerate(s):  # 枚举子串右端点
            cnt[c] -= 1
            while all(cnt[x] <= m for x in "QWER"):
                ans = min(ans, right - left + 1)
                cnt[s[left]] += 1
                left += 1  # 缩小子串
        return ans

    # 20230214
    # https://leetcode.cn/problems/longest-well-performing-interval/
    # https://leetcode.cn/problems/longest-well-performing-interval/solution/python3javacgo-yi-ti-yi-jie-qian-zhui-he-0os2/
    def longestWPI(self, hours: List[int]) -> int:
            ans = s = 0
            pos = {}
            for i, x in enumerate(hours):
                s += 1 if x > 8 else -1
                if s > 0:
                    ans = i + 1
                elif s - 1 in pos:
                    ans = max(ans, i - pos[s - 1])
                if s not in pos:
                    pos[s] = i
            return ans
    
    # 20230215
    # https://leetcode.cn/problems/check-if-it-is-a-good-array/
    # https://leetcode.cn/problems/check-if-it-is-a-good-array/solution/python3javacgo-yi-ti-yi-jie-shu-xue-pei-3f4da/
    def isGoodArray(self, nums: List[int]) -> bool:
        return reduce(gcd, nums) == 1
    
    # 20230216
    # https://leetcode.cn/problems/maximum-number-of-pairs-in-array/
    def numberOfPairs(self, nums: List[int]) -> List[int]:
        ans = [0, 0]
        for i in range(101):
            c = nums.count(i)
            ans[0] += c//2
        ans[1] = len(nums)-ans[0]*2
        return ans
    
    # 20230217
    # https://leetcode.cn/problems/largest-1-bordered-square/
    # https://leetcode.cn/problems/largest-1-bordered-square/solution/tu-jie-qian-zhui-he-qing-song-acpythonja-5mnn/
    def largest1BorderedSquare(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        rs = [list(accumulate(row, initial=0)) for row in grid]  # 每行的前缀和
        cs = [list(accumulate(col, initial=0)) for col in zip(*grid)]  # 每列的前缀和
        for d in range(min(m, n), 0, -1):  # 从大到小枚举正方形边长 d
            for i in range(m - d + 1):
                for j in range(n - d + 1):  # 枚举正方形左上角坐标 (i,j)
                    # 上 左 下 右 四条边 1 的个数均为 d
                    if rs[i][j + d] - rs[i][j] == d and \
                       cs[j][i + d] - cs[j][i] == d and \
                       rs[i + d - 1][j + d] - rs[i + d - 1][j] == d and \
                       cs[j + d - 1][i + d] - cs[j + d - 1][i] == d:
                        return d * d
        return 0

    # 20230218
    # https://leetcode.cn/problems/find-positive-integer-solution-for-a-given-equation/
    # https://leetcode.cn/problems/find-positive-integer-solution-for-a-given-equation/solution/zhao-chu-gei-ding-fang-cheng-de-zheng-zh-kie6/
    def findSolution(self, customfunction: 'CustomFunction', z: int) -> List[List[int]]:
        ans = []
        y = 1000
        for x in range(1, 1001):
            while y and customfunction.f(x, y) > z:
                y -= 1
            if y == 0:
                break
            if customfunction.f(x, y) == z:
                ans.append([x, y])
        return ans

    # 20230219
    # https://leetcode.cn/problems/maximum-average-pass-ratio/
    # https://leetcode.cn/problems/maximum-average-pass-ratio/solution/python3javacgo-yi-ti-yi-jie-you-xian-dui-qrmo/
    def maxAverageRatio(self, classes: List[List[int]], extraStudents: int) -> float:
        h = [(a / b - (a + 1) / (b + 1), a, b) for a, b in classes]
        heapify(h)
        for _ in range(extraStudents):
            _, a, b = heappop(h)
            a, b = a + 1, b + 1
            heappush(h, (a / b - (a + 1) / (b + 1), a, b))
        return sum(v[1] / v[2] for v in h) / len(classes)

    # 20230220
    # https://leetcode.cn/problems/best-poker-hand/
    # https://leetcode.cn/problems/best-poker-hand/solution/python3javacgo-yi-ti-yi-jie-ji-shu-by-lc-i8vb/
    def bestHand(self, ranks: List[int], suits: List[str]) -> str:
        if all(a == b for a, b in pairwise(suits)):
            return 'Flush'
        cnt = Counter(ranks)
        if any(v >= 3 for v in cnt.values()):
            return 'Three of a Kind'
        if any(v == 2 for v in cnt.values()):
            return 'Pair'
        return 'High Card'
    
    # 20230221
    # https://leetcode.cn/problems/minimum-number-of-taps-to-open-to-water-a-garden/
    # https://leetcode.cn/problems/minimum-number-of-taps-to-open-to-water-a-garden/solution/guan-gai-hua-yuan-de-zui-shao-shui-long-tou-shu-3/
    def minTaps(self, n: int, ranges: List[int]) -> int:
        intervals = []
        for i, r in enumerate(ranges):
            start = max(0, i - r)
            end = min(n, i + r)
            intervals.append((start, end))
        intervals.sort()

        dp = [inf] * (n + 1)
        dp[0] = 0
        for start, end in intervals:
            if dp[start] == inf:
                return -1
            for j in range(start, end + 1):
                dp[j] = min(dp[j], dp[start] + 1)
        return dp[n]

    # 20230222
    # https://leetcode.cn/problems/stone-game-ii/
    # https://leetcode.cn/problems/stone-game-ii/solution/jiao-ni-yi-bu-bu-si-kao-dong-tai-gui-hua-jjax/
    def stoneGameII(self, piles: List[int]) -> int:
        s, n = 0, len(piles)
        f = [[0] * (n + 1) for _ in range(n)]
        for i in range(n - 1, -1, -1):
            s += piles[i]
            for m in range(1, i // 2 + 2):
                if i + m * 2 >= n:
                    f[i][m] = s
                else:
                    f[i][m] = s - min(f[i + x][max(m, x)] for x in range(1, m * 2 + 1))
        return f[0][1]

    # 20230223
    # https://leetcode.cn/problems/circular-permutation-in-binary-representation/
    # https://leetcode.cn/problems/circular-permutation-in-binary-representation/solution/python3-grey-code-by-lyncien/
    def circularPermutation(self, n: int, start: int) -> List[int]:
        N = pow(2, n)
        res = [0] * N
        for i in range(N):
            res[i] = i ^ (i >> 1) 
        p = 2 << (n - 1)
        start_bin = 0
        while p:
            start_bin |= (start_bin >> 1 & p) ^ (start & p)
            p >>= 1
        return res[start_bin:] + res[:start_bin]

    # 20230224
    # https://leetcode.cn/problems/make-array-zero-by-subtracting-equal-amounts/
    # https://leetcode.cn/problems/make-array-zero-by-subtracting-equal-amounts/solution/shi-shu-zu-zhong-suo-you-yuan-su-du-deng-ix12/
    def minimumOperations(self, nums: List[int]) -> int:
        return len(set(nums) - {0})

    # 20230225
    # https://leetcode.cn/problems/minimum-swaps-to-make-strings-equal/
    # https://leetcode.cn/problems/minimum-swaps-to-make-strings-equal/solution/jiao-huan-zi-fu-shi-de-zi-fu-chuan-xiang-6b1u/
    def minimumSwap(self, s1: str, s2: str) -> int:
        xy, yx = 0, 0
        n = len(s1)
        for a, b in zip(s1, s2):
            if a == 'x' and b == 'y':
                xy += 1
            if a == 'y' and b == 'x':
                yx += 1
        if (xy + yx) % 2 == 1:
            return -1
        return xy // 2 + yx // 2 + xy % 2 + yx % 2
    
    # 20230226
    # https://leetcode.cn/problems/maximum-score-words-formed-by-letters/
    # https://leetcode.cn/problems/maximum-score-words-formed-by-letters/solution/python3javacgo-yi-ti-yi-jie-er-jin-zhi-m-nv0j/
    def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int:
        cnt = Counter(letters)
        n = len(words)
        ans = 0
        for i in range(1 << n):
            cur = Counter(''.join([words[j] for j in range(n) if i >> j & 1]))
            if all(v <= cnt[c] for c, v in cur.items()):
                t = sum(v * score[ord(c) - ord('a')] for c, v in cur.items())
                ans = max(ans, t)
        return ans

    # 20230227
    # https://leetcode.cn/problems/decrease-elements-to-make-array-zigzag/
    # https://leetcode.cn/problems/decrease-elements-to-make-array-zigzag/solution/mei-you-si-lu-yi-bu-bu-ti-shi-ni-si-kao-cm0h2/
    def movesToMakeZigzag(self, nums: List[int]) -> int:
        s = [0] * 2
        for i, x in enumerate(nums):
            left = nums[i - 1] if i else inf
            right = nums[i + 1] if i < len(nums) - 1 else inf
            s[i % 2] += max(x - min(left, right) + 1, 0)
        return min(s)

    # 20230228
    # https://leetcode.cn/problems/merge-similar-items/
    def mergeSimilarItems(self, items1: List[List[int]], items2: List[List[int]]) -> List[List[int]]:
        item = {}
        for v, w in items1:
            item[v] = w
        for v, w in items2:
            if v in item:
                item[v] += w
            else:
                item[v] = w
        items3 = []
        for k, v in item.items():
            items3.append([k, v])
        items3.sort()
        return items3
    
    # 20230301
    # https://leetcode.cn/problems/largest-local-values-in-a-matrix/
    def largestLocal(self, grid: List[List[int]]) -> List[List[int]]:
        ans = []
        for i in range(1, len(grid)-1):
            ansx = []
            for j in range(1, len(grid)-1):
                ansx.append(max(grid[i-1][j-1], grid[i-1][j], grid[i-1][j+1], grid[i][j-1], grid[i][j], grid[i][j+1], grid[i+1][j-1], grid[i+1][j], grid[i+1][j+1]))
            ans.append(ansx)
        return ans

    # 20230302
    # https://leetcode.cn/problems/bianry-number-to-string-lcci/
    # https://leetcode.cn/problems/bianry-number-to-string-lcci/solution/python3javacgo-yi-ti-yi-jie-shi-jin-zhi-ool7l/
    def printBin(self, num: float) -> str:
        ans = '0.'
        while len(ans) < 32 and num:
            num *= 2
            x = int(num)
            ans += str(x)
            num -= x
        return 'ERROR' if num else ans

    # 20230303
    # https://leetcode.cn/problems/making-file-names-unique/
    def getFolderNames(self, names: List[str]) -> List[str]:
        dic = {}
        ans = []
        for name in names:
            if name in ans:
                i = dic[name]
                while f"{name}({i})" in dic:
                    i += 1
                ans.append(f"{name}({i})")
                dic[f"{name}({i})"] = 1
                dic[name] = i
            else:
                ans.append(name)
                dic[name]=1
        return ans 

    # 20230304
    # https://leetcode.cn/problems/triples-with-bitwise-and-equal-to-zero/
    # https://leetcode.cn/problems/triples-with-bitwise-and-equal-to-zero/solution/you-ji-qiao-de-mei-ju-chang-shu-you-hua-daxit/
    def countTriplets(self, nums: List[int]) -> int:
        cnt = Counter(x & y for x in nums for y in nums)
        return sum(c for x, c in cnt.items() for y in nums if x & y == 0)

    # 20230305
    # https://leetcode.cn/problems/maximum-profit-of-operating-a-centennial-wheel/
    # https://leetcode.cn/problems/maximum-profit-of-operating-a-centennial-wheel/solution/python3javacgo-yi-ti-yi-jie-mo-ni-by-lcb-6kz7/
    def minOperationsMaxProfit(self, customers: List[int], boardingCost: int, runningCost: int) -> int:
        ans = -1
        mx = t = 0
        wait = 0
        i = 0
        while wait or i < len(customers):
            wait += customers[i] if i < len(customers) else 0
            up = wait if wait < 4 else 4
            wait -= up
            t += up * boardingCost - runningCost
            i += 1
            if t > mx:
                mx = t
                ans = i
        return ans

    # 20230306
    # https://leetcode.cn/problems/minimum-deletions-to-make-string-balanced/
    # https://leetcode.cn/problems/minimum-deletions-to-make-string-balanced/solution/python3javacgo-yi-ti-shuang-jie-dong-tai-5ej8/
    def minimumDeletions(self, s: str) -> int:
        n = len(s)
        f = [0] * (n + 1)
        b = 0
        for i, c in enumerate(s, 1):
            if c == 'b':
                f[i] = f[i - 1]
                b += 1
            else:
                f[i] = min(f[i - 1] + 1, b)
        return f[n]

    # 20230307
    # https://leetcode.cn/problems/brace-expansion-ii/
    # https://leetcode.cn/problems/brace-expansion-ii/solution/python3javacgotypescript-yi-ti-yi-jie-di-gs64/
    def braceExpansionII(self, expression: str) -> List[str]:
        def dfs(exp):
            j = exp.find('}')
            if j == -1:
                s.add(exp)
                return
            i = exp.rfind('{', 0, j - 1)
            a, c = exp[:i], exp[j + 1:]
            for b in exp[i + 1: j].split(','):
                dfs(a + b + c)

        s = set()
        dfs(expression)
        return sorted(s)
        
    # 20230308
    # https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof/
    # https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof/solution/mian-shi-ti-47-li-wu-de-zui-da-jie-zhi-dong-tai-gu/
    def maxValue(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        for j in range(1, n): # 初始化第一行
            grid[0][j] += grid[0][j - 1]
        for i in range(1, m): # 初始化第一列
            grid[i][0] += grid[i - 1][0]
        for i in range(1, m):
            for j in range(1, n):
                grid[i][j] += max(grid[i][j - 1], grid[i - 1][j])
        return grid[-1][-1]
        
    # 20230309
    # https://leetcode.cn/problems/minimum-recolors-to-get-k-consecutive-black-blocks/
    # https://leetcode.cn/problems/minimum-recolors-to-get-k-consecutive-black-blocks/solution/python3javacgotypescript-yi-ti-yi-jie-hu-dmdz/
    def minimumRecolors(self, blocks: str, k: int) -> int:
        ans = cnt = blocks[:k].count('W')
        for i in range(k, len(blocks)):
            cnt += blocks[i] == 'W'
            cnt -= blocks[i - k] == 'W'
            ans = min(ans, cnt)
        return ans
    
    # 20230310
    # https://leetcode.cn/problems/make-sum-divisible-by-p/
    # https://leetcode.cn/problems/make-sum-divisible-by-p/solution/python3javacgotypescript-yi-ti-yi-jie-qi-ay3t/
    def minSubarray(self, nums: List[int], p: int) -> int:
        k = sum(nums) % p
        if k == 0:
            return 0
        last = {0: -1}
        cur = 0
        ans = len(nums)
        for i, x in enumerate(nums):
            cur = (cur + x) % p
            target = (cur - k + p) % p
            if target in last:
                ans = min(ans, i - last[target])
            last[cur] = i
        return -1 if ans == len(nums) else ans
    
    # 20230311
    # https://leetcode.cn/problems/find-longest-subarray-lcci/
    # https://leetcode.cn/problems/find-longest-subarray-lcci/solution/python3javacgotypescript-yi-ti-yi-jie-qi-qy7i/
    def findLongestSubarray(self, array: List[str]) -> List[str]:
        vis = {0: -1}
        s = mx = k = 0
        for i, x in enumerate(array):
            s += 1 if x.isalpha() else -1
            if s in vis:
                if mx < i - (j := vis[s]):
                    mx = i - j
                    k = j + 1
            else:
                vis[s] = i
        return array[k: k + mx]

    # 20230312
    # https://leetcode.cn/problems/count-subtrees-with-max-distance-between-cities/
    # https://leetcode.cn/problems/count-subtrees-with-max-distance-between-cities/solution/zhuang-tai-ya-suo-wei-yun-suan-by-shan-z-egto/
    def countSubgraphsForEachDiameter(self, n: int, edges: List[List[int]]) -> List[int]:
        # dp[i]存储对应子树的最大距离，将i用2进制表示，1表示该节点在子树中，0表示不在
        dp = [0]*(1<<n)
        # dist[i][j]表示两点间的距离
        dist = [[n+1]*n for i in range(n)]
        for i in range(n):
            dist[i][i] = 0

        # 初始化
        for x,y in edges:
           dp[(1<<(x-1))+(1<<(y-1))] = 1
           dist[x-1][y-1] = 1
           dist[y-1][x-1] = 1
        
        # floyd算法求任意两点间的距离
        for k in range(n):
            for i in range(n):
                for j in range(n):
                    if dist[i][k]+dist[k][j] < dist[i][j]:
                        dist[i][j] = dist[i][k]+dist[k][j]
        
        for i in range(1<<n):
            # 子树i不连通则continue
            if dp[i] == 0:
                continue
            # 试图将新节点加入子树i中
            for j in range(n):
                # 子树i中已包含j节点，或者子树已被考虑过，则continue
                if ((1<<j)&i) != 0 or dp[i+(1<<j)] !=0:
                    continue
                # 检查子树i中是否有和j相邻的点
                for k in range(n):
                    if ((1<<k)&i) != 0 and dist[j][k] == 1:
                        dp[i+(1<<j)] = dp[i]
                        break
                # 没有相邻的点，则continue
                if dp[i+(1<<j)] == 0:
                    continue
                # 计算节点j到子树i中节点的最远距离
                for k in range(n):
                    if ((1<<k)&i) != 0:
                        dp[i+(1<<j)] = max(dp[i+(1<<j)], dist[j][k])
        
        ans = [0]*(n-1)
        for i in range(1<<n):
            if dp[i]!=0:
                ans[dp[i]-1]+=1
        return ans

    # 20230313
    # https://leetcode.cn/problems/minimum-hours-of-training-to-win-a-competition/
    # https://leetcode.cn/problems/minimum-hours-of-training-to-win-a-competition/solution/python-2383-ying-de-bi-sai-xu-yao-de-zui-4ybe/
    def minNumberOfHours(self, initialEnergy: int, initialExperience: int, energy: List[int], experience: List[int]) -> int:
        ans = 0
        for eny, exp in zip(energy, experience):
            if initialEnergy <= eny:
                ans += eny + 1 - initialEnergy
                initialEnergy = eny + 1
            if initialExperience <= exp:
                ans += exp + 1 - initialExperience
                initialExperience = exp + 1
            initialEnergy -= eny
            initialExperience += exp
        return ans

    # 20230314
    # https://leetcode.cn/problems/find-valid-matrix-given-row-and-column-sums/
    # https://leetcode.cn/problems/find-valid-matrix-given-row-and-column-sums/solution/mei-you-si-lu-yi-ge-dong-hua-miao-dong-f-eezj/
    def restoreMatrix(self, rowSum: List[int], colSum: List[int]) -> List[List[int]]:
        m, n = len(rowSum), len(colSum)
        mat = [[0] * n for _ in range(m)]
        for i, rs in enumerate(rowSum):
            for j, cs in enumerate(colSum):
                mat[i][j] = x = min(rs, cs)
                rs -= x
                colSum[j] -= x
        return mat

    # 20230315
    # https://leetcode.cn/problems/maximal-network-rank/
    # https://leetcode.cn/problems/maximal-network-rank/solution/tu-zui-da-wang-luo-zhi-python-by-yuer-fl-uqev/
    def maximalNetworkRank(self, n: int, roads: List[List[int]]) -> int:
        adjs = defaultdict(set)
        for u,v in roads:
            adjs[u].add(v)
            adjs[v].add(u)
        max_rank = 0
        for i in range(n):
            for j in range(i+1,n):
                rank=len(adjs[i])+len(adjs[j])
                if j in adjs[i]:
                    rank-=1
                max_rank=max(max_rank, rank)
        return max_rank

    # 20230316
    # https://leetcode.cn/problems/count-subarrays-with-median-k/
    # https://leetcode.cn/problems/count-subarrays-with-median-k/solution/deng-jie-zhuan-huan-pythonjavacgo-by-end-5w11/
    def countSubarrays(self, nums: List[int], k: int) -> int:
        pos, n = nums.index(k), len(nums)
        cnt, x = [0] * (n * 2), n
        cnt[x] = 1
        for i in range(pos - 1, -1, -1):  # 从 pos-1 开始累加 x
            x += 1 if nums[i] < k else -1
            cnt[x] += 1

        ans, x = cnt[n] + cnt[n - 1], n
        for i in range(pos + 1, len(nums)):  # 从 pos+1 开始累加 x
            x += 1 if nums[i] > k else -1
            ans += cnt[x] + cnt[x - 1]
        return ans

    # 20230317
    # https://leetcode.cn/problems/longest-subsequence-with-limited-sum/
    # https://leetcode.cn/problems/longest-subsequence-with-limited-sum/solution/2389-he-you-xian-de-zui-chang-zi-xu-lie-rmg4l/
    def answerQueries(self, nums: List[int], queries: List[int]) -> List[int]:
        n, m = len(queries), len(nums)
        ans = [0]*n
        nums.sort()
        sum_nums = list(accumulate(nums))
        for i, e in enumerate(queries):
            left, right = 0, m-1
            while left < right:
                mid = left+right+1 >> 1
                if sum_nums[mid] <= e:
                    left = mid
                else:
                    right = mid-1
            if sum_nums[left] <= e:
                ans[i] = left+1
            else:
                ans[i] = 0
        return ans

    # 20230318
    # https://leetcode.cn/problems/split-two-strings-to-make-palindrome/
    # https://leetcode.cn/problems/split-two-strings-to-make-palindrome/solution/by-liupengsay-ivna/
    def checkPalindromeFormation(self, a: str, b: str) -> bool:
        if a == a[::-1] or b == b[::-1]:
            return True
        n = len(a)
        i = 0
        j = n - 1
        while i < j and a[i] == b[j]:
            i += 1
            j -= 1
        if i >= j or a[i:j + 1] == a[i:j + 1][::-1] or b[i:j + 1] == b[i:j + 1][::-1]:
            return True

        i = 0
        j = n - 1
        while i < j and b[i] == a[j]:
            i += 1
            j -= 1
        if i >= j or a[i:j + 1] == a[i:j + 1][::-1] or b[i:j + 1] == b[i:j + 1][::-1]:
            return True

        return False

    # 20230319
    # https://leetcode.cn/problems/lexicographically-smallest-string-after-applying-operations/
    # https://leetcode.cn/problems/lexicographically-smallest-string-after-applying-operations/solution/python3javacgo-yi-ti-shuang-jie-bfs-bao-xl8n2/
    def findLexSmallestString(self, s: str, a: int, b: int) -> str:
        q = deque([s])
        vis = {s}
        ans = s
        while q:
            s = q.popleft()
            if ans > s:
                ans = s
            t1 = ''.join([str((int(c) + a) % 10) if i & 1 else c for i, c in enumerate(s)])
            t2 = s[-b:] + s[:-b]
            for t in (t1, t2):
                if t not in vis:
                    vis.add(t)
                    q.append(t)
        return ans  
    
    # 20230320
    # https://leetcode.cn/problems/numbers-with-repeated-digits/
    # https://leetcode.cn/problems/numbers-with-repeated-digits/solution/python3qiu-wu-zhong-fu-shu-zi-de-ge-shu-by-gorilla/
    def numDupDigitsAtMostN(self, N: int) -> int:
        if N <= 10:  return 0
        x = [1]
        for i in range(10): # x[i] = i!
            x.append(x[-1]*(i+1))
        m = len(str(N))
        logits = list(map(int, str(N)))
        count = 0   # 无重复数字的个数
        # 1. 1<=length<m
        for i in range(1, m):   # 9*A(9, i-1) for all 1<=i<m
            count += 9*x[9]//x[10-i]
        # 2. length==m
        count += (logits[0]-1)*x[9]//x[10-m]    # case 0: 1<=a[0]<logits[0]
        for i in range(1, m):   # case i: a[k]==logits[k] for all 0<=k<i
            for j in range(logits[i]):  # select a[i]<logits[i] and a[i]!=a[k] for all 0<=k<i
                if j not in logits[:i]: 
                    count += x[9-i] // x[10-m]
            if logits[i] in logits[:i]: # let a[i]=logits[i]
                break
        if i == m-1 and logits[m-1] not in logits[:m-1]:
            count += 1
        return N-count

    # 20230321
    # https://leetcode.cn/problems/convert-the-temperature/
    def convertTemperature(self, celsius: float) -> List[float]:
        return [celsius+273.15, celsius*1.8+32]

    # 20230322
    # https://leetcode.cn/problems/best-team-with-no-conflicts/
    # https://leetcode.cn/problems/best-team-with-no-conflicts/solution/zui-chang-di-zeng-zi-xu-lie-cong-on2-dao-ojqu/
    def bestTeamScore(self, scores: List[int], ages: List[int]) -> int:
        a = sorted(zip(scores, ages))
        f = [0] * len(a)
        for i, (score, age) in enumerate(a):
            for j in range(i):
                if a[j][1] <= age:
                    f[i] = max(f[i], f[j])
            f[i] += score
        return max(f)

    # 20230323
    # https://leetcode.cn/problems/arithmetic-subarrays/
    # https://leetcode.cn/problems/arithmetic-subarrays/solution/python3-deng-chai-zi-shu-zu-mo-ni-by-com-2gt5/
    def checkArithmeticSubarrays(self, nums: List[int], l: List[int], r: List[int]) -> List[bool]:
        n = len(r)
        ans = [False] * n

        for i in range(n):
            t = sorted(nums[l[i]:r[i] + 1])
            d = t[1] - t[0]
            flag = True
            for j in range(1, len(t)):
                if t[j] - t[j -1] != d:
                    flag = False
                    break
            ans[i] = flag
        return ans
    
    # 20230325
    # https://leetcode.cn/problems/shortest-subarray-to-be-removed-to-make-array-sorted/
    # https://leetcode.cn/problems/shortest-subarray-to-be-removed-to-make-array-sorted/solution/python3javacgo-yi-ti-shuang-jie-shuang-z-85bh/
    def findLengthOfShortestSubarray(self, arr: List[int]) -> int:
        n = len(arr)
        i, j = 0, n - 1
        while i + 1 < n and arr[i] <= arr[i + 1]:
            i += 1
        while j - 1 >= 0 and arr[j - 1] <= arr[j]:
            j -= 1
        if i >= j:
            return 0
        ans = min(n - i - 1, j)
        for l in range(i + 1):
            r = bisect_left(arr, arr[l], lo=j)
            ans = min(ans, r - l - 1)
        return ans
    
    # 20230326
    # https://leetcode.cn/problems/find-subarrays-with-equal-sum/
    # https://leetcode.cn/problems/find-subarrays-with-equal-sum/solution/python3javacgotypescript-yi-ti-yi-jie-ha-gcx7/
    def findSubarrays(self, nums: List[int]) -> bool:
        vis = set()
        for a, b in pairwise(nums):
            if (x := a + b) in vis:
                return True
            vis.add(x)
        return False

    # 20230327
    # https://leetcode.cn/problems/count-substrings-that-differ-by-one-character/
    # https://leetcode.cn/problems/count-substrings-that-differ-by-one-character/solution/python3javacgo-yi-ti-shuang-jie-mei-ju-y-zmin/
    def countSubstrings(self, s: str, t: str) -> int:
        ans = 0
        m, n = len(s), len(t)
        for i, a in enumerate(s):
            for j, b in enumerate(t):
                if a != b:
                    l = r = 0
                    while i > l and j > l and s[i - l - 1] == t[j - l - 1]:
                        l += 1
                    while i + r + 1 < m and j + r + 1 < n and s[i + r + 1] == t[j + r + 1]:
                        r += 1
                    ans += (l + 1) * (r + 1)
        return ans

    # 20230328
    # https://leetcode.cn/problems/shortest-common-supersequence/
    # https://leetcode.cn/problems/shortest-common-supersequence/solution/er-xu-cheng-ming-jiu-xu-zui-by-liupengsa-k2a5/
    def shortestCommonSupersequence(self, str1: str, str2: str) -> str:
        m, n = len(str1), len(str2)
        dp = [[0] * (n + 1) for _ in range(m + 1)]
        for i in range(m - 1, -1, -1):
            for j in range(n - 1, -1, -1):
                dp[i][j] = max(dp[i + 1][j], dp[i][j + 1], dp[i + 1][j + 1] + int(str1[i] == str2[j]))
        
        # 使用双指针确定一个最长的公共序列索引对
        ind = deque()
        i = j = 0
        while len(ind) < dp[0][0]:
            length = dp[0][0] - len(ind)
            while dp[i + 1][j] >= length:
                i += 1
            while str2[j] != str1[i]:
                j += 1
            ind.append([i, j])
            i += 1
            j += 1
        
        # 按照索引对依次加入到结果当中
        ans = ""
        i = j = 0
        while ind:
            while i < ind[0][0]:
                ans += str1[i]
                i += 1
            while j < ind[0][1]:
                ans += str2[j]
                j += 1
            ind.popleft()
            ans += str1[i]
            i += 1
            j += 1
        ans += str1[i:] + str2[j:]
        return ans
    
    # 20230329
    # https://leetcode.cn/problems/count-sorted-vowel-strings/
    # https://leetcode.cn/problems/count-sorted-vowel-strings/solution/python3javacgo-yi-ti-shuang-jie-ji-yi-hu-vh2j/
    def countVowelStrings(self, n: int) -> int:
        f = [1] * 5
        for _ in range(n - 1):
            s = 0
            for j in range(5):
                s += f[j]
                f[j] = s
        return sum(f)
        
    # 20230330
    # https://leetcode.cn/problems/widest-vertical-area-between-two-points-containing-no-points/
    # https://leetcode.cn/problems/widest-vertical-area-between-two-points-containing-no-points/solution/python3javacgo-yi-ti-shuang-jie-pai-xu-t-pc0a/
    def maxWidthOfVerticalArea(self, points: List[List[int]]) -> int:
        nums = [x for x, _ in points]
        n = len(nums)
        mi, mx = min(nums), max(nums)
        bucket_size = max(1, (mx - mi) // (n - 1))
        bucket_count = (mx - mi) // bucket_size + 1
        buckets = [[inf, -inf] for _ in range(bucket_count)]
        for x in nums:
            i = (x - mi) // bucket_size
            buckets[i][0] = min(buckets[i][0], x)
            buckets[i][1] = max(buckets[i][1], x)
        ans = 0
        prev = inf
        for curmin, curmax in buckets:
            if curmin > curmax:
                continue
            ans = max(ans, curmin - prev)
            prev = curmax
        return ans

    # 20230331
    # https://leetcode.cn/problems/number-of-arithmetic-triplets/
    # https://leetcode.cn/problems/number-of-arithmetic-triplets/solution/lai-zi-bei-da-suan-fa-ke-de-leetcodeti-j-cada/
    def arithmeticTriplets(self, nums: List[int], diff: int) -> int:
        res = 0
        for i in range(0, len(nums)-2):
            for j in range(i+1, len(nums)-1):
                for k in range(j+1, len(nums)):
                    if nums[j] - nums[i] == nums[k] - nums[j] == diff:
                        res += 1
        return res

    # 20230401
    # https://leetcode.cn/problems/masking-personal-information/
    # https://leetcode.cn/problems/masking-personal-information/solution/python3javacgo-yi-ti-yi-jie-mo-ni-by-lcb-0u7z/
    def maskPII(self, s: str) -> str:
        if s[0].isalpha():
            s = s.lower()
            return s[0] + '*****' + s[s.find('@') - 1:]
        s = ''.join(c for c in s if c.isdigit())
        cnt = len(s) - 10
        suf = '***-***-' + s[-4:]
        return suf if cnt == 0 else f'+{"*" * cnt}-{suf}'   

    # 20230402
    # https://leetcode.cn/problems/minimum-score-triangulation-of-polygon/
    # https://leetcode.cn/problems/minimum-score-triangulation-of-polygon/solution/jian-dan-dfsji-yi-hua-yu-dong-tai-gui-hua-by-amcho/
    def minScoreTriangulation(self, A: List[int]) -> int:
        length = len(A)
        inf = float('inf')
        dp = [[inf for _ in range(length)] for _ in range(length)]

        for i in range(length - 1):
            dp[i][i + 1] = 0

        for d in range(2, length):
            for i in range(0, length - d):
                j = i + d
                for k in range(i + 1, j):
                    dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j] + A[i] * A[k] * A[j])

        return dp[0][length - 1]

    # 20230403
    # https://leetcode.cn/problems/previous-permutation-with-one-swap/
    # https://leetcode.cn/problems/previous-permutation-with-one-swap/solution/python3javacgotypescript-yi-ti-yi-jie-ta-pxxt/
    def prevPermOpt1(self, arr: List[int]) -> List[int]:
        n = len(arr)
        for i in range(n - 1, 0, -1):
            if arr[i - 1] > arr[i]:
                for j in range(n - 1, i - 1, -1):
                    if arr[j] < arr[i - 1] and arr[j] != arr[j - 1]:
                        arr[i - 1], arr[j] = arr[j], arr[i - 1]
                        return arr
        return arr

    # 20230404
    # https://leetcode.cn/problems/minimum-cost-to-merge-stones/
    # https://leetcode.cn/problems/minimum-cost-to-merge-stones/solution/qu-jian-dp-by-skyhood/
    def mergeStones(self, stones: List[int], K: int) -> int:
        N = len(stones)
        if (N - 1) % (K - 1): return -1
        prefix = [0] * (N+1)
        for i in range(1,N+1): prefix[i] = stones[i-1] + prefix[i-1]
        dp = [[0] * N for _ in range(N)]
        for m in range(K, N+1):
            for i in range(N-m+1):
                dp[i][i+m-1] = min(dp[i][k] + dp[k+1][i+m-1] for k in range(i, i+m-1, K-1)) + (prefix[i+m] - prefix[i] if (m-1)%(K-1) == 0 else 0)
        return dp[0][N-1]

    # 20230405
    # https://leetcode.cn/problems/number-of-common-factors/
    # https://leetcode.cn/problems/number-of-common-factors/solution/python3javacgotypescript-yi-ti-shuang-ji-ou0n/
    def commonFactors(self, a: int, b: int) -> int:
        g = gcd(a, b)
        ans, x = 0, 1
        while x * x <= g:
            if g % x == 0:
                ans += 1
                ans += x * x < g
            x += 1
        return ans

    # 20230406
    # https://leetcode.cn/problems/convert-to-base-2/
    # https://leetcode.cn/problems/convert-to-base-2/solution/python3-duan-chu-fa-fu-er-jin-zhi-zhuan-1zahd/
    def baseNeg2(self, n: int) -> str:
        if n == 0: return '0'               # 特例
        x, y, ans = n, 0, []
        while x:
            y = x - (((x - 1) // -2) * -2)  # 余数
            x = (x - 1) // -2               # 上取整
            ans.append(str(y))

        return ''.join(ans[::-1])
    
    # 20230407
    # https://leetcode.cn/problems/moving-stones-until-consecutive-ii/
    # https://leetcode.cn/problems/moving-stones-until-consecutive-ii/solution/tu-jie-xia-tiao-qi-pythonjavacgo-by-endl-r1eb/
    def numMovesStonesII(self, s: List[int]) -> List[int]:
        s.sort()
        n = len(s)
        e1 = s[-2] - s[0] - n + 2
        e2 = s[-1] - s[1] - n + 2  # 计算空位
        max_move = max(e1, e2)
        if e1 == 0 or e2 == 0:  # 特殊情况：没有空位
            return [min(2, max_move), max_move]
        max_cnt = left = 0
        for right, x in enumerate(s):  # 滑动窗口：枚举右端点
            while s[left] <= x - n:  # 窗口大小大于 n
                left += 1
            max_cnt = max(max_cnt, right - left + 1)  # 维护窗口内的最大石子数
        return [n - max_cnt, max_move]

    # 20230408
    # https://leetcode.cn/problems/smallest-sufficient-team/
    # https://leetcode.cn/problems/smallest-sufficient-team/solution/python3javacgotypescript-yi-ti-yi-jie-zh-gf06/
    def smallestSufficientTeam(self, req_skills: List[str], people: List[List[str]]) -> List[int]:
        d = {s: i for i, s in enumerate(req_skills)}
        m, n = len(req_skills), len(people)
        p = [0] * n
        for i, ss in enumerate(people):
            for s in ss:
                p[i] |= 1 << d[s]
        f = [inf] * (1 << m)
        g = [0] * (1 << m)
        h = [0] * (1 << m)
        f[0] = 0
        for i in range(1 << m):
            if f[i] == inf:
                continue
            for j in range(n):
                if f[i] + 1 < f[i | p[j]]:
                    f[i | p[j]] = f[i] + 1
                    g[i | p[j]] = j
                    h[i | p[j]] = i
        i = (1 << m) - 1
        ans = []
        while i:
            ans.append(g[i])
            i = h[i]
        return ans
    
    # 20230409
    # https://leetcode.cn/problems/check-distances-between-same-letters/submissions/
    # https://leetcode.cn/problems/check-distances-between-same-letters/solution/pythonyi-xing-jian-ji-shi-xian-2399-jian-qbbn/
    def checkDistances(self, s: str, distance: List[int]) -> bool:
        return all(len(s.split(c)[1]) == distance[ord(c) - 97] for c in set(s))

    # 20230410
    # https://leetcode.cn/problems/next-greater-node-in-linked-list/
    # https://leetcode.cn/problems/next-greater-node-in-linked-list/solution/1019lian-biao-zhong-de-xia-yi-ge-geng-da-8299/
    def nextLargerNodes(self, head: Optional[ListNode]) -> List[int]:
        stack = list()
        ref = list()
        while head:
            ref.append(head.val)
            head = head.next
        nlen = len(ref)
        res = [0] * nlen
        ref.reverse()
        stack.append(ref[0])
        for i in range(1,nlen):
            if stack and stack[-1] > ref[i]:
                res[i] = stack[-1]
            else:
                while stack and stack[-1] <= ref[i]:
                    stack.pop()
                if stack and stack[-1] > ref[i]:
                    res[i] = stack[-1]
            stack.append(ref[i])
        res.reverse()
        return res

    # 20230411
    # https://leetcode.cn/problems/robot-bounded-in-circle/
    # https://leetcode.cn/problems/robot-bounded-in-circle/solution/python3-si-ci-gui-ling-kun-yu-huan-zhong-5l1k/
    def isRobotBounded(self, instructions: str) -> bool:
        cur = [0, 0]  # 当前位置
        d = 0  # 方向 0123 -> 上右下左
        n = len(instructions)
        for i in range(n * 4):
            match instructions[i % n]:
                case 'R': d = (d + 1) % 4  # 右转
                case 'L': d = (d - 1) % 4  # 左转
                case 'G':
                    match d:
                        case 0: cur[1] += 1
                        case 1: cur[0] += 1
                        case 2: cur[1] -= 1
                        case 3: cur[0] -= 1
            if cur == [0, 0] and i % n == n - 1: return True
        return False   

    # 20230412
    # https://leetcode.cn/problems/longest-chunked-palindrome-decomposition/
    # https://leetcode.cn/problems/longest-chunked-palindrome-decomposition/solution/tu-jie-tan-xin-zuo-fa-yi-tu-miao-dong-py-huik/
    def longestDecomposition(self, s: str) -> int:
        ans = 0
        while s:
            i = 1
            while i <= len(s) // 2 and s[:i] != s[-i:]:  # 枚举前后缀
                i += 1
            if i > len(s) // 2:  # 无法分割
                ans += 1
                break
            ans += 2  # 分割出 s[:i] 和 s[-i:]
            s = s[i:-i]
        return ans

    # 20230413
    # https://leetcode.cn/problems/most-frequent-even-element/
    # https://leetcode.cn/problems/most-frequent-even-element/solution/pythonjava-2404-chu-xian-zui-pin-fan-de-wg8dm/
    def mostFrequentEven(self, nums: List[int]) -> int:
        h = Counter(nums)
        ak, av = -1, 0
        for k, v in h.items():
            if k & 1 == 0:
                if v > av:
                    av = v
                    ak = k
                elif v == av and k < ak:
                    ak = k
        return ak
    
    # 20230414
    # https://leetcode.cn/problems/camelcase-matching/
    # https://leetcode.cn/problems/camelcase-matching/solution/shuang-zhi-zhen-pi-pei-by-1233588-fymm/
    def camelMatch(self, queries: list[str], pattern: str) -> list[bool]:
        answerlist=[True]*len(queries)#初始化list[bool]列表
        n =len(queries)#检验串列表的长度
        for num in range(n):#对检验串列表的每一个检验串进行遍历
            p=0#初始化模式串的指针
            for pos in queries[num]:#遍历检验串的每一个字符
                if p<len(pattern)and pattern[p]==pos:#如果检验串的字符匹配此时模式串指针指向的模式串字符，匹配成功，指针往下移动
                    p+=1
                elif pos.isupper():#不匹配指针指向的模式串字符，且是大写字母的话，匹配失败
                    answerlist[num]=False
                    break
            if p < len(pattern):#遍历完检验串，依旧没有完全匹配模式串，匹配是被
                answerlist[num] = False
        return answerlist#返回list[bool]列表
    
    # 20230415
    # https://leetcode.cn/problems/flower-planting-with-no-adjacent/
    # https://leetcode.cn/problems/flower-planting-with-no-adjacent/solution/bu-lin-jie-zhi-hua-by-leetcode-solution-bv74/
    def gardenNoAdj(self, n: int, paths: List[List[int]]) -> List[int]:
        adj = [[] for i in range(n)]
        for path in paths:
            adj[path[0] - 1].append(path[1] - 1)
            adj[path[1] - 1].append(path[0] - 1)
        ans = [0] * n
        for i in range(n):
            colored = [False] * 5
            for vertex in adj[i]:
                colored[ans[vertex]] = True
            for j in range(1, 5):
                if not colored[j]:
                    ans[i] = j
                    break
        return ans   

    # 20230417
    # https://leetcode.cn/problems/count-days-spent-together/
    # https://leetcode.cn/problems/count-days-spent-together/solution/python3lai-xie-de-zhu-yao-shi-zhe-ge-by-dqolf/
    def countDaysTogether(self, arriveAlice: str, leaveAlice: str, arriveBob: str, leaveBob: str) -> int:
        tian_list = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        arriveAlicedata = list(map(int, arriveAlice.split("-")))
        leaveAlicedata = list(map(int, leaveAlice.split("-")))
        arriveBobdata = list(map(int, arriveBob.split("-")))
        leaveBobdata = list(map(int, leaveBob.split("-")))
        if arriveBobdata[0] * 100 + arriveBobdata[1] > leaveAlicedata[0] * 100 + leaveAlicedata[1] or arriveAlicedata[
            0] * 100 + arriveAlicedata[1] > leaveBobdata[0] * 100 + leaveBobdata[1]:
            return 0  #把肯定没有共度天数的可能先if ,return 0出来
        else:
            #用先离开的时间减去后到达的时间，就可以得到共度天数
            BobleaveTian = 0
            for i in range(leaveBobdata[0] - 1):
                BobleaveTian = BobleaveTian + tian_list[i]
            BobleaveTian = BobleaveTian + leaveBobdata[1]

            #得到Bob离开的时间,从一年的第一天算起

            AliceleaveTian = 0
            for i in range(leaveAlicedata[0] - 1):
                AliceleaveTian = AliceleaveTian + tian_list[i]
            AliceleaveTian = AliceleaveTian + leaveAlicedata[1]

            #得到Alice离开的时间,从一年的第一天算起

            a = min(BobleaveTian, AliceleaveTian)  #a代表两人先离开对应的时间(对应天数)

            AlicearriveTian = 0
            for i in range(arriveAlicedata[0] - 1):
                AlicearriveTian = AlicearriveTian + tian_list[i]
            AlicearriveTian = AlicearriveTian + arriveAlicedata[1]
            # 得到Alice到达的时间,从一年的第一天算起

            BobarriveTian = 0
            for i in range(arriveBobdata[0] - 1):
                BobarriveTian = BobarriveTian + tian_list[i]
            BobarriveTian = BobarriveTian + arriveBobdata[1]
            # 得到Bob到达的时间,从一年的第一天算起

            b = max(AlicearriveTian, BobarriveTian)  #b代表后到达的时间

            return a - b + 1 #返回共度时间

    # 20230418
    # https://leetcode.cn/problems/maximum-difference-between-node-and-ancestor/
    # https://leetcode.cn/problems/maximum-difference-between-node-and-ancestor/solution/jie-dian-yu-qi-zu-xian-zhi-jian-de-zui-d-iafd/
    def maxAncestorDiff(self, root: TreeNode) -> int:
        maxdiff=0
        stack=[root]
        stackval=[root.val]
        cur=root
        while stack:
            node=stack[-1]
            if node.left and node.left!=cur and node.right!=cur:
                stack.append(node.left)
                stackval.append(node.left.val)
            elif node.right and node.right!=cur:
                stack.append(node.right)
                stackval.append(node.right.val)
            else:
                if not node.left and not node.right:
                    maxdiff=max(maxdiff,max(stackval)-min(stackval))
                cur=stack.pop()
                stackval.pop()
        return maxdiff

    # 20230419
    # https://leetcode.cn/problems/partition-array-for-maximum-sum/
    # https://leetcode.cn/problems/partition-array-for-maximum-sum/solution/by-int_64-aszu/
    def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int:
        n = len(arr)
        dp = [0]*n
        
        for i in range(k): 
            dp[i] = max(arr[:i+1])*(i+1)
        
        for i in range(k,n):
            dp[i] = max(dp[i-j-1] + max(arr[(i-j):(i+1)]) * (j+1) for j in range(k))
        return dp[-1]  
    
    # 20230420
    # https://leetcode.cn/problems/make-array-strictly-increasing/
    # https://leetcode.cn/problems/make-array-strictly-increasing/solution/yi-wei-dp-si-lu-xiang-jie-cpy3-by-newhar/
    def makeArrayIncreasing(self, arr1: List[int], arr2: List[int]) -> int:
        # 预处理：排序，去重，加哨兵
        maxv = 1000000000
        arr1 = [-1] + arr1 + [maxv + 5]
        arr2 = sorted(list(set(arr2)))
        n = len(arr1)

        dp = [0] + [maxv]*(n-1)
        for i in range(1,n):
            j = bisect_left(arr2, arr1[i])
            for k in range(1, min(i-1, j) + 1):  # 1. 枚举替换的个数 k = 1 to min(i-1,j)
                if arr1[i-k-1] < arr2[j-k]:
                    dp[i] = min(dp[i], dp[i-k-1] + k)
            if arr1[i-1] < arr1[i]:          # 2. 不替换 arr1[i-1]
                dp[i] = min(dp[i], dp[i-1])

        return dp[-1] if dp[-1] < maxv else -1

    # 20230421
    # https://leetcode.cn/problems/smallest-even-multiple/
    # https://leetcode.cn/problems/smallest-even-multiple/solution/javapython3yi-xing-dai-ma-zhi-jie-miao-s-gv8u/
    def smallestEvenMultiple(self, n: int) -> int:
        return n if (n & 1 == 0) else n * 2    

    # 20230422
    # https://leetcode.cn/problems/longest-arithmetic-subsequence/    
    # https://leetcode.cn/problems/longest-arithmetic-subsequence/solution/dp-zi-dian-cun-chu-suo-you-ke-neng-de-go-mez5/
    def longestArithSeqLength(self, nums: List[int]) -> int:
        dp = [{} for _ in range(len(nums))] # 初始化dp数组,每个元素为一个字典

        for i in range(1, len(nums)): # 从1开始遍历数组
            for j in range(i): # 遍历之前出现的数字
                bias = nums[i] - nums[j] # 求出公差
                if bias in dp[j]: # 如果公差在dp[j]中出现了
                    dp[i][bias] = dp[j][bias] + 1 # 那么直接加一即可
                else:
                    dp[i][bias] = 2 # 如果不存在，则初始化为2
        
        max_ret = 0
        for i in range(len(nums)): # 遍历找出最大值
            if len(dp[i].keys()) > 0:
                max_ret = max(max_ret, max(dp[i].values()))

        return max_ret

    # 20230423
    # https://leetcode.cn/problems/filling-bookcase-shelves/
    # https://leetcode.cn/problems/filling-bookcase-shelves/solution/dong-tai-gui-hua-python3-by-smoon1989/    
    def minHeightShelves(self, books: List[List[int]], shelf_width: int) -> int:
        n = len(books)
        dp = [1000000] * (n + 1)
        dp[0] = 0
        for i in range(1, n + 1):
            tmp_width, j, h = 0, i, 0
            while j > 0:
                tmp_width += books[j - 1][0]
                if tmp_width > shelf_width:
                    break
                h = max(h, books[j - 1][1])
                dp[i] = min(dp[i], dp[j - 1] + h)
                j -= 1
        return dp[-1]

    # 20230424
    # https://leetcode.cn/problems/last-substring-in-lexicographical-order/
    # https://leetcode.cn/problems/last-substring-in-lexicographical-order/solution/python3javacgotypescript-yi-ti-yi-jie-sh-3amj/
    def lastSubstring(self, s: str) -> str:
        i, j, k = 0, 1, 0
        while j + k < len(s):
            if s[i + k] == s[j + k]:
                k += 1
            elif s[i + k] < s[j + k]:
                i += k + 1
                k = 0
                if i >= j:
                    j = i + 1
            else:
                j += k + 1
                k = 0
        return s[i:]

    # 20230425
    # https://leetcode.cn/problems/sort-the-people/
    # https://leetcode.cn/problems/sort-the-people/solution/by-tang-chao-7c-bbfa/
    def sortPeople(self, names: List[str], heights: List[int]) -> List[str]:
        return [i[0] for i in sorted(list(zip(names,heights)),key= lambda x: x[1],reverse=True)]
    
    # 20230426
    # https://leetcode.cn/problems/maximum-sum-of-two-non-overlapping-subarrays/
    # https://leetcode.cn/problems/maximum-sum-of-two-non-overlapping-subarrays/solution/python3javacgotypescript-yi-ti-yi-jie-qi-7nqt/
    def maxSumTwoNoOverlap(self, nums: List[int], firstLen: int, secondLen: int) -> int:
        n = len(nums)
        s = list(accumulate(nums, initial=0))
        ans = t = 0
        i = firstLen
        while i + secondLen - 1 < n:
            t = max(t, s[i] - s[i - firstLen])
            ans = max(ans, t + s[i + secondLen] - s[i])
            i += 1
        t = 0
        i = secondLen
        while i + firstLen - 1 < n:
            t = max(t, s[i] - s[i - secondLen])
            ans = max(ans, t + s[i + firstLen] - s[i])
            i += 1
        return ans

    # 20230427
    # https://leetcode.cn/problems/longest-string-chain/
    # https://leetcode.cn/problems/longest-string-chain/solution/python3javacgotypescript-yi-ti-yi-jie-pa-qrie/
    def longestStrChain(self, words: List[str]) -> int:
        words.sort(key=len)
        d = defaultdict(int)
        ans = 0
        for s in words:
            x = 1
            for i in range(len(s)):
                t = s[:i] + s[i + 1:]
                x = max(x, d[t] + 1)
            d[s] = x
            ans = max(ans, x)
        return ans

    # 20230429
    # https://leetcode.cn/problems/remove-letter-to-equalize-frequency/
    # https://leetcode.cn/problems/remove-letter-to-equalize-frequency/solution/python3javacgotypescript-yi-ti-yi-jie-ji-7vtu/
    def equalFrequency(self, word: str) -> bool:
        cnt = Counter(word)
        for c in cnt.keys():
            cnt[c] -= 1
            if len(set(v for v in cnt.values() if v)) == 1:
                return True
            cnt[c] += 1
        return False

    # 20230430
    # https://leetcode.cn/problems/moving-stones-until-consecutive/
    # https://leetcode.cn/problems/moving-stones-until-consecutive/solution/python3javacgotypescript-yi-ti-yi-jie-fe-qc34/
    def numMovesStones(self, a: int, b: int, c: int) -> List[int]:
        x, y, z = sorted((a, b, c))
        mi = mx = 0
        if z - x > 2:
            mi = 1 if y - x < 3 or z - y < 3 else 2
            mx = z - x - 2
        return [mi, mx]

    # 20230501
    # https://leetcode.cn/problems/time-needed-to-inform-all-employees/
    # https://leetcode.cn/problems/time-needed-to-inform-all-employees/solution/python-zi-di-xiang-shang-jian-zhi-by-gaussic/
    def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int:
        mins = [0] * n
        for i in range(n):
            cur_i, cur_min = i, 0
            # print(cur_i, end=' ')
            while manager[cur_i] != -1:
                cur_i = manager[cur_i]
                # print(cur_i, end=' ')
                cur_min += informTime[cur_i]
            mins[i] = cur_min + mins[cur_i]
            manager[i] = -1
            # print(manager)
            # print('min', cur_min)
        return max(mins)

    # 20230502
    # https://leetcode.cn/problems/powerful-integers/
    # https://leetcode.cn/problems/powerful-integers/solution/python3javacgotypescript-yi-ti-yi-jie-ha-javr/
    def powerfulIntegers(self, x: int, y: int, bound: int) -> List[int]:
        ans = set()
        a = 1
        while a <= bound:
            b = 1
            while a + b <= bound:
                ans.add(a + b)
                b *= y
                if y == 1:
                    break
            if x == 1:
                break
            a *= x
        return list(ans)
    
    # 20230503
    # https://leetcode.cn/problems/check-if-word-is-valid-after-substitutions/
    # https://leetcode.cn/problems/check-if-word-is-valid-after-substitutions/solution/python3javacgotypescript-yi-ti-yi-jie-zh-80tk/
    def isValid(self, s: str) -> bool:
        if len(s) % 3:
            return False
        t = []
        for c in s:
            t.append(c)
            if ''.join(t[-3:]) == 'abc':
                t[-3:] = []
        return not t

    # 20230504
    # https://leetcode.cn/problems/maximum-fruits-harvested-after-at-most-k-steps/
    # https://leetcode.cn/problems/maximum-fruits-harvested-after-at-most-k-steps/solution/hua-dong-chuang-kou-jian-ji-xie-fa-pytho-1c2d/
    def maxTotalFruits(self, fruits: List[List[int]], startPos: int, k: int) -> int:
        left = bisect_left(fruits, [startPos - k])  # 向左最远能到 fruits[left][0]
        right = bisect_left(fruits, [startPos + 1])  # startPos 右边最近水果（因为下面求的是左闭右开区间）
        ans = s = sum(c for _, c in fruits[left:right])  # 从 fruits[left][0] 到 startPos 的水果数
        while right < len(fruits) and fruits[right][0] <= startPos + k:
            s += fruits[right][1]  # 枚举最右位置为 fruits[right][0]
            while fruits[right][0] * 2 - fruits[left][0] - startPos > k and \
                  fruits[right][0] - fruits[left][0] * 2 + startPos > k:
                s -= fruits[left][1]  # fruits[left][0] 无法到达
                left += 1
            ans = max(ans, s)  # 更新答案最大值
            right += 1  # 继续枚举下一个最右位置
        return ans
    
    # 20230505
    # https://leetcode.cn/problems/the-employee-that-worked-on-the-longest-task/
    # https://leetcode.cn/problems/the-employee-that-worked-on-the-longest-task/solution/javapython3zhi-jie-mo-ni-by-lxk1203-p4zg/
    def hardestWorker(self, n: int, logs: List[List[int]]) -> int:
        id_ = n         # 用时最长任务的员工id
        last_time = 0   # 用时最长的任务的用时 初始为0 
        longest = 0     # 记录上一个任务完成的时刻 初始为0
        for log in logs:    
            used = log[1] - last_time   # 计算当前任务的用时
            if used > longest:
                # 当前任务用时used于当前最长用时longest：更新longest和id
                longest = used
                id_ = log[0]
            elif used == longest:
                #  当前任务用时used等于当前最长用时longest：更新id为更小的那一个id
                id_ = min(id_, log[0])
            last_time = log[1]  #  更新上一个任务完成的时刻
        return id_
    
    # 20230506
    # https://leetcode.cn/problems/minimum-number-of-frogs-croaking/
    # https://leetcode.cn/problems/minimum-number-of-frogs-croaking/solution/python3-bao-cun-bu-tong-wei-zhi-de-qing-wa-shu-shi/
    def minNumberOfFrogs(self, croakOfFrogs: str) -> int:
        order = 'croak'
        ret = 0
        frogs = [0,0,0,0,0] # 表示每一个字母位置的青蛙数
        for c in croakOfFrogs:
            i = order.index(c) # 找到对应字母的index，字母较少这样写起来比较快
            if frogs[i-1] == 0:
                if i != 0: # 如果前一个字母没有青蛙且不是c，则不成立
                    return -1
            else:
                frogs[i-1] -= 1 # 前一个字母青蛙数-1
            frogs[i] += 1 # 当前字母青蛙数+1
        return -1 if any(frogs[:4]) else frogs[-1]    

    # 20230507
    # https://leetcode.cn/problems/pairs-of-songs-with-total-durations-divisible-by-60/
    # https://leetcode.cn/problems/pairs-of-songs-with-total-durations-divisible-by-60/solution/python3javacgotypescript-yi-ti-yi-jie-sh-8m9z/
    def numPairsDivisibleBy60(self, time: List[int]) -> int:
        cnt = Counter()
        ans = 0
        for x in time:
            x %= 60
            y = (60 - x) % 60
            ans += cnt[y]
            cnt[x] += 1
        return ans

    # 20230508
    # https://leetcode.cn/problems/minimum-moves-to-move-a-box-to-their-target-location/
    # https://leetcode.cn/problems/minimum-moves-to-move-a-box-to-their-target-location/solution/er-xu-cheng-ming-jiu-xu-zui-by-liupengsa-w2rb/
    def minPushBox(self, grid: List[List[str]]) -> int:
        m, n = len(grid), len(grid[0])

        # 寻找位置初始化
        start, box, target = [-1, -1], [-1, -1], [-1, -1]
        for i in range(m):
            for j in range(n):
                if grid[i][j] == "S":
                    start = [i, j]
                    grid[i][j] = "."
                elif grid[i][j] == "B":
                    box = [i, j]
                    grid[i][j] = "."
                elif grid[i][j] == "T":
                    target = [i, j]
                    grid[i][j] = "."

        # 定义广度优先搜索的状态
        visit = {(0, start[0], start[1], box[0], box[1])}
        pre = [[0, start[0], start[1], box[0], box[1]]]
        nex = []
        while pre or nex:
            if not pre:
                # 两个队列状态的距离永远相差1
                pre = nex
                nex = []
            dis, i, j, x, y = pre.pop()
            if [x, y] == target:
                return dis
            for a, b in [[-1, 0], [1, 0], [0, 1], [0, -1]]:
                if 0 <= i + a < m and 0 <= j + b < n:
                    if i + a == x and j + b == y:
                        # 与箱子临近且可以推
                        if 0 <= x + a < m and 0 <= y + b < n and grid[x + a][y + b] != "#" and (i + a, j + b, x + a, y + b) not in visit:
                            visit.add((i + a, j + b, x + a, y + b))
                            nex.append([dis + 1, i + a, j + b, x + a, y + b])
                    else:
                        # 只能人走一步不算次数
                        if grid[i + a][j + b] != "#" and (i + a, j + b, x, y) not in visit:
                            visit.add((i + a, j + b, x, y))
                            pre.append([dis, i + a, j + b, x, y])
        return -1

    # 20230509
    # https://leetcode.cn/problems/number-of-valid-clock-times/
    # https://leetcode.cn/problems/number-of-valid-clock-times/solution/bao-li-mei-ju-bi-dui-python100-2437-you-mhiw3/
    def countTime(self, time: str) -> int:
        cnth, cntm = 0, 0
        for i in range(24):
            if time[0] in ('?', str(i // 10)) and time[1] in ('?', str(i % 10)): cnth += 1
        for i in range(60):
            if time[3] in ('?', str(i // 10)) and time[4] in ('?', str(i % 10)): cntm += 1
        return cnth * cntm

    # 20230510
    # https://leetcode.cn/problems/smallest-integer-divisible-by-k/
    # https://leetcode.cn/problems/smallest-integer-divisible-by-k/solution/san-chong-suan-fa-you-hua-pythonjavacgo-tk4cj/
    def smallestRepunitDivByK(self, k: int) -> int:
        seen = set()
        x = 1 % k
        while x and x not in seen:
            seen.add(x)
            x = (x * 10 + 1) % k
        return -1 if x else len(seen) + 1       

    # 20230511
    # https://leetcode.cn/problems/binary-string-with-substrings-representing-1-to-n/
    # https://leetcode.cn/problems/binary-string-with-substrings-representing-1-to-n/solution/san-chong-suan-fa-cong-bao-li-dao-you-hu-nmtq/
    def queryString(self, s: str, n: int) -> bool:
        return all(bin(i)[2:] in s for i in range(1, n + 1))

    # 20230512
    # https://leetcode.cn/problems/reverse-subarray-to-maximize-array-value/
    # https://leetcode.cn/problems/reverse-subarray-to-maximize-array-value/solution/python3javacgotypescript-yi-ti-yi-jie-fe-3ygf/ 
    def maxValueAfterReverse(self, nums: List[int]) -> int:
        ans = s = sum(abs(x - y) for x, y in pairwise(nums))
        for x, y in pairwise(nums):
            ans = max(ans, s + abs(nums[0] - y) - abs(x - y))
            ans = max(ans, s + abs(nums[-1] - x) - abs(x - y))
        for k1, k2 in pairwise((1, -1, -1, 1, 1)):
            mx, mi = -inf, inf
            for x, y in pairwise(nums):
                a = k1 * x + k2 * y
                b = abs(x - y)
                mx = max(mx, a - b)
                mi = min(mi, a + b)
            ans = max(ans, s + max(mx - mi, 0))
        return ans
    
    # 20230513
    # https://leetcode.cn/problems/largest-positive-integer-that-exists-with-its-negative/
    # https://leetcode.cn/problems/largest-positive-integer-that-exists-with-its-negative/solution/python3javacgotypescript-yi-ti-yi-jie-ha-k3hx/
    def findMaxK(self, nums: List[int]) -> int:
        s = set(nums)
        return max((x for x in s if -x in s), default=-1)

    # 20230514
    # https://leetcode.cn/problems/distant-barcodes/
    # https://leetcode.cn/problems/distant-barcodes/solution/python3javacgotypescript-yi-ti-yi-jie-ji-3or2/
    def rearrangeBarcodes(self, barcodes: List[int]) -> List[int]:
        cnt = Counter(barcodes)
        barcodes.sort(key=lambda x: (-cnt[x], x))
        n = len(barcodes)
        ans = [0] * len(barcodes)
        ans[::2] = barcodes[: (n + 1) // 2]
        ans[1::2] = barcodes[(n + 1) // 2:]
        return ans   
        
    # 20230515
    # https://leetcode.cn/problems/flip-columns-for-maximum-number-of-equal-rows/
    # https://leetcode.cn/problems/flip-columns-for-maximum-number-of-equal-rows/solution/ni-xiang-si-wei-pythonjavacgo-by-endless-915k/
    def maxEqualRowsAfterFlips(self, matrix: List[List[int]]) -> int:
        cnt = Counter()
        for row in matrix:
            if row[0]:  # 翻转第一个数为 1 的行
                for j in range(len(row)):
                    row[j] ^= 1
            cnt[tuple(row)] += 1
        return max(cnt.values())    
        
    # 20230516
    # https://leetcode.cn/problems/minimum-difficulty-of-a-job-schedule/
    # https://leetcode.cn/problems/minimum-difficulty-of-a-job-schedule/solution/by-zhou-pen-cheng-ab2b/
    def minDifficulty(self, job: List[int], d: int) -> int:
        n = len(job)
        if n <= d: return -1 if n < d else sum(job)
        g = [[-1] * n for _ in range(n)]
        for i in range(n):
            for j in range(i, n):
                g[i][j] = max(g[i][j - 1], job[j])

        f = [[float('inf')] * (d + 1) for _ in range(n + 1)]
        f[0][0] = 0
        for i in range(1, n + 1):
            for j in range(1, min(i + 1, d + 1)):
                for k in range(1, i - j + 2):
                    f[i][j] = min(f[i][j], f[i - k][j - 1] + g[i - k][i - 1])
        return f[n][d]

    # 20230517
    # https://leetcode.cn/problems/determine-if-two-events-have-conflict/
    # https://leetcode.cn/problems/determine-if-two-events-have-conflict/solution/python3javacgotypescript-yi-ti-yi-jie-zi-timk/
    def haveConflict(self, event1: List[str], event2: List[str]) -> bool:
        return not (event1[0] > event2[1] or event1[1] < event2[0])

    
    # 20230518
    # https://leetcode.cn/problems/adding-two-negabinary-numbers/
    # https://leetcode.cn/problems/adding-two-negabinary-numbers/solution/fu-er-jin-zhi-xiang-jia-shuang-zhi-zhen-zfth7/
    def addNegabinary(self, arr1: List[int], arr2: List[int]) -> List[int]:
        carry = 0
        res = []
        i = len(arr1) - 1
        j = len(arr2) - 1
        
        while i >= 0 or j >= 0 or carry != 0:
            if i >= 0:
                carry += arr1[i]
                i -= 1
            if j >= 0:
                carry += arr2[j]
                j -= 1
            
            res.append(carry & 1)
            carry = -(carry >> 1)
        
        while len(res) > 1 and res[-1] == 0:
            res.pop()
        
        res.reverse()
        return res
    
    # 20230519
    # https://leetcode.cn/problems/letter-tile-possibilities/
    # https://leetcode.cn/problems/letter-tile-possibilities/solution/on2-ji-shu-dppythonjavacgo-by-endlessche-hmez/
    def numTilePossibilities(self, tiles: str) -> int:
        f = [1] + [0] * len(tiles)
        n = 0
        for cnt in Counter(tiles).values():  # 枚举第 i 种字母
            n += cnt  # 常数优化：相比从 len(tiles) 开始要更快
            for j in range(n, 0, -1):  # 枚举序列长度 j
                # 枚举第 i 种字母选了 k 个，注意 k=0 时的方案数已经在 f[j] 中了
                for k in range(1, min(j, cnt) + 1):
                    f[j] += f[j - k] * comb(j, k)  # comb 也可以预处理，见其它语言的实现
        return sum(f[1:])

    # 20230520
    # https://leetcode.cn/problems/maximum-sum-bst-in-binary-tree/
    # https://leetcode.cn/problems/maximum-sum-bst-in-binary-tree/solution/hou-xu-bian-li-pythonjavacgo-by-endlessc-gll3/
    def maxSumBST(self, root: Optional[TreeNode]) -> int:
        ans = 0  # 二叉搜索树可以为空

        def dfs(node: Optional[TreeNode]) -> Tuple:
            if node is None:
                return inf, -inf, 0

            l_min, l_max, l_sum = dfs(node.left)  # 递归左子树
            r_min, r_max, r_sum = dfs(node.right)  # 递归右子树
            x = node.val
            if x <= l_max or x >= r_min:  # 不是二叉搜索树
                return -inf, inf, 0

            s = l_sum + r_sum + x  # 这棵子树的所有节点值之和
            nonlocal ans
            ans = max(ans, s)

            return min(l_min, x), max(r_max, x), s

        dfs(root)
        return ans
    
    # 20230521
    # https://leetcode.cn/problems/o8SXZn/
    # https://leetcode.cn/problems/o8SXZn/solution/python3javacgotypescript-yi-ti-yi-jie-ta-awvh/
    def storeWater(self, bucket: List[int], vat: List[int]) -> int:
        mx = max(vat)
        if mx == 0:
            return 0
        ans = inf
        for x in range(1, mx + 1):
            y = sum(max(0, (v + x - 1) // x - b) for v, b in zip(vat, bucket))
            ans = min(ans, x + y)
        return ans

    # 20230522
    # https://leetcode.cn/problems/insufficient-nodes-in-root-to-leaf-paths/
    # https://leetcode.cn/problems/insufficient-nodes-in-root-to-leaf-paths/solution/dfsshen-du-you-xian-sou-su-pythonshuang-eqgil/
    def sufficientSubset(self, root, limit):
        def dfs(node, limit_):
            if node.left == None and node.right == None: return node.val < limit_
            fl, fr = -1 , -1
            if node.left:
                if dfs(node.left , limit_ - node.val): node.left , fl = None, 1
                else: fl = 0
            if node.right:
                if dfs(node.right, limit_ - node.val): node.right, fr = None, 1
                else: fr = 0
            return fl != 0 and fr != 0
        if dfs(root, limit): root = None
        return root          

    # 20230523
    # https://leetcode.cn/problems/largest-values-from-labels/
    # https://leetcode.cn/problems/largest-values-from-labels/solution/python3javacgotypescript-yi-ti-yi-jie-ta-evaq/
    def largestValsFromLabels(self, values: List[int], labels: List[int], numWanted: int, useLimit: int) -> int:
        ans = num = 0
        cnt = Counter()
        for v, l in sorted(zip(values, labels), reverse=True):
            if cnt[l] < useLimit:
                cnt[l] += 1
                num += 1
                ans += v
                if num == numWanted:
                    break
        return ans

    # 20230524
    # https://leetcode.cn/problems/frog-position-after-t-seconds/
    # https://leetcode.cn/problems/frog-position-after-t-seconds/solution/bfsha-xi-pythonjin-shuang-bai-1377-t-mia-3uqp/
    def frogPosition(self, n, edges, t, target):
        dct, lst = defaultdict(set), [[1, 0, 1]]
        for a, b in edges:
            dct[a].add(b)
            dct[b].add(a)
        for _ in range(t):
            lst_ = []
            for i, parent, v in lst:
                dct[i].discard(parent)
                if i == target: return 0 if dct[i] else 1.0 / v 
                for j in dct[i]: lst_.append([j, i, v * len(dct[i])])
            lst = lst_
        for i, parent, v in lst:
            if i == target: return 1.0 / v
        return 0       
    
    # 20230525
    # https://leetcode.cn/problems/odd-string-difference/
    # https://leetcode.cn/problems/odd-string-difference/solution/2451chai-zhi-shu-zu-bu-tong-de-zi-fu-chu-t8q1/
    def oddString(self, words: List[str]) -> str:
        dic = dict()
        for w in words:
            t = [ord(w[i]) - ord(w[i-1]) for i in range(1,len(w))]
            ref = ",".join(map(str,t))
            if ref not in dic:
                dic[ref] = [w]
            else:
                dic[ref] += [w]
        return [v[0] for v in dic.values() if len(v) == 1][0]

    # 20230526
    # https://leetcode.cn/problems/shortest-path-in-binary-matrix/
    # https://leetcode.cn/problems/shortest-path-in-binary-matrix/solution/by-jam007-q6po/
    def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
        n = len(grid)
        if grid[0][0] == 1 or grid[n-1][n-1] == 1:
            return -1

        queue = [(0, 0, 1)]
        # 走过的点标记为 1, 避免重复计算提高算法效率
        grid[0][0] = 1
        dir = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]
        for i, j, cnt in queue:
            if i == n-1 and j == n-1: 
                return cnt
            for dx, dy in dir:
                x, y = i+dx, j+dy
                if 0 <= x < n and 0 <= y < n and grid[x][y] == 0:
                    grid[x][y] = 1
                    queue.append((x, y, cnt+1))
        return -1

    # 20230527
    # https://leetcode.cn/problems/statistics-from-a-large-sample/
    # https://leetcode.cn/problems/statistics-from-a-large-sample/solution/ge-shu-fen-bie-mo-ni-ji-suan-python100-1-tu1u/
    def sampleStats(self, count: List[int]) -> List[float]:
        n, lst = sum(count), list(accumulate(count))
        for minimum in range(256):
            if count[minimum] != 0: break
        for maximum in range(255, -1, -1):
            if count[maximum] != 0: break    
        mean = sum(i * c for i, c in enumerate(count)) / n    
        median = bisect.bisect_left(lst, (n >> 1) + 1) if n & 1 else (bisect.bisect_left(lst, n >> 1) + bisect.bisect_left(lst, (n >> 1) + 1)) / 2.0
        mode = count.index(max(count))
        return minimum, maximum, mean, median, mode
    
    # 20230528
    # https://leetcode.cn/problems/find-the-kth-smallest-sum-of-a-matrix-with-sorted-rows/
    # https://leetcode.cn/problems/find-the-kth-smallest-sum-of-a-matrix-with-sorted-rows/solution/san-chong-suan-fa-bao-li-er-fen-da-an-du-k1vd/
    def kthSmallest(self, mat: List[List[int]], k: int) -> int:
        a = mat[0][:k]
        for row in mat[1:]:
            a = sorted(x + y for x in a for y in row)[:k]
        return a[-1]

    # 20230529
    # https://leetcode.cn/problems/average-value-of-even-numbers-that-are-divisible-by-three/
    # https://leetcode.cn/problems/average-value-of-even-numbers-that-are-divisible-by-three/solution/ji-suan-bei-6zheng-chu-pythonliang-xing-brgx7/
    def averageValue(self, nums: List[int]) -> int:
        nums_ = [n for n in nums if n % 6 == 0]
        return sum(nums_) // len(nums_) if nums_ else 0

    # 20230530
    # https://leetcode.cn/problems/delete-nodes-and-return-forest/
    # https://leetcode.cn/problems/delete-nodes-and-return-forest/solution/he-shi-ji-lu-da-an-pythonjavacgo-by-endl-lpcd/
    def delNodes(self, root: Optional[TreeNode], to_delete: List[int]) -> List[TreeNode]:
        ans = []
        s = set(to_delete)
        def dfs(node: Optional[TreeNode]) -> Optional[TreeNode]:
            if node is None: return None
            node.left = dfs(node.left)
            node.right = dfs(node.right)
            if node.val not in s: return node
            if node.left: ans.append(node.left)
            if node.right: ans.append(node.right)
            return None
        if dfs(root): ans.append(root)
        return ans

    # 20230531
    # https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values/
    # https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values/solution/python3-tan-xin-1130-xie-zhi-de-zui-xiao-1t57/
    def mctFromLeafValues(self, arr: List[int]) -> int:
        ans = 0
        while len(arr) > 1:
            # mn 记录（相邻元素最大值）的最小值 
            # del_i 记录删除元素的下标
            mn = inf
            del_i = 0
            for i in range(len(arr) - 1):
                if max(arr[i], arr[i + 1]) < mn:
                    mn = max(arr[i], arr[i + 1])
                    del_i = i + 1 if arr[i] > arr[i + 1] else i
            ans += mn * arr[del_i]

            # 删掉最小元素 保留最大元素
            del arr[del_i]

        return ans

    # 20230601
    # https://leetcode.cn/problems/maximum-tastiness-of-candy-basket/
    # https://leetcode.cn/problems/maximum-tastiness-of-candy-basket/solution/er-fen-sou-suo-da-an-tan-xin-yan-zheng-b-0gc5/
    def maximumTastiness(self, price: List[int], k: int) -> int:
        price.sort()
        n = len(price)
        l,r = 0,price[-1]-price[0]
        def validation(v):
            nonlocal k,price
            cur = price[0]
            cnt = k-1            
            while cnt:
                j = bisect_left(price, cur+v)
                if j==n: return False
                cur = price[j]
                cnt-=1
            return True

        while l<r:
            mid = l+r+1 >> 1
            if validation(mid):
                l = mid
            else:
                r = mid-1
        return l

    # 20230602
    # https://leetcode.cn/problems/count-vowel-strings-in-ranges/
    # https://leetcode.cn/problems/count-vowel-strings-in-ranges/solution/python3javacgotypescript-yi-ti-shuang-ji-lhef/
    def vowelStrings(self, words: List[str], queries: List[List[int]]) -> List[int]:
        vowels = set("aeiou")
        nums = [i for i, w in enumerate(words) if w[0] in vowels and w[-1] in vowels]
        return [bisect_right(nums, r) - bisect_left(nums, l) for l, r in queries]   
        
    # 20230603
    # https://leetcode.cn/problems/swap-for-longest-repeated-character-substring/
    # https://leetcode.cn/problems/swap-for-longest-repeated-character-substring/solution/python3javacgotypescript-yi-ti-yi-jie-sh-uq9g/
    def maxRepOpt1(self, text: str) -> int:
        cnt = Counter(text)
        n = len(text)
        ans = i = 0
        while i < n:
            j = i
            while j < n and text[j] == text[i]:
                j += 1
            l = j - i
            k = j + 1
            while k < n and text[k] == text[i]:
                k += 1
            r = k - j - 1
            ans = max(ans, min(l + r + 1, cnt[text[i]]))
            i = j
        return ans     

    # 2030604
    # https://leetcode.cn/problems/number-of-distinct-averages/
    # https://leetcode.cn/problems/number-of-distinct-averages/solution/python3javacgotypescript-yi-ti-yi-jie-pa-g8td/
    def distinctAverages(self, nums: List[int]) -> int:
        nums.sort()
        ans = 0
        cnt = Counter()
        for i in range(len(nums) >> 1):
            x = nums[i] + nums[-i - 1]
            cnt[x] += 1
            if cnt[x] == 1:
                ans += 1
        return ans    

    # 20230605
    # https://leetcode.cn/problems/apply-operations-to-an-array/
    # https://leetcode.cn/problems/apply-operations-to-an-array/solution/bao-li-mo-ni-bian-li-ji-suan-2460-dui-sh-ynr6/
    def applyOperations(self, nums: List[int]) -> List[int]:
        n = len(nums)
        for i in range(n - 1):
            if nums[i] == nums[i + 1]:
                nums[i] <<= 1
                nums[i + 1] = 0
        return [x for x in nums if x != 0] + [0] * nums.count(0)
        
    # 20230606
    # https://leetcode.cn/problems/equal-row-and-column-pairs/
    # https://leetcode.cn/problems/equal-row-and-column-pairs/solution/bao-li-bi-jiao-ji-shu-pythonyi-xing-2352-bher/
    def equalPairs(self, grid: List[List[int]]) -> int:
        return len([1 for c in zip(*grid) for r in grid if tuple(r) == c])

    # 20230607
    # https://leetcode.cn/problems/mice-and-cheese/
    # https://leetcode.cn/problems/mice-and-cheese/solution/python3gopython3xing-zhi-jie-pai-xu-ji-k-v73u/
    def miceAndCheese(self, reward1: List[int], reward2: List[int], k: int) -> int:
        scores = [(reward1[i] - reward2[i], i) for i in range(len(reward1))]
        scores.sort(reverse=True)  # 按照得分差降序排序
        return sum(reward1[i] for _, i in scores[:k]) + sum([reward2[i] for _, i in scores[k:]]) # 返回前k个reward1的总和加上计算剩余reward2的总和

    # 20230608
    # https://leetcode.cn/problems/tiling-a-rectangle-with-the-fewest-squares/
    # https://leetcode.cn/problems/tiling-a-rectangle-with-the-fewest-squares/solution/gan-jue-shi-ge-shu-xue-wen-ti-er-bu-shi-g35n1/
    def tilingRectangle(self, n, m):
        m,n = max(m,n),min(m,n)
        if n == 6 and m == 11:
            return 6
        if n == 11 and m == 13:
            return 6
        if n == 6 and m == 7:
            return 5
        if n == 9 and m == 10:
            return 6
        if n == 10 and m == 11:
            return 6
        if n == 11 and m == 12:
            return 7
        if n == 12 and m == 13:
            return 7
        if m == n:
            return 1
        if m>n:
            m=m-n
            return self.tilingRectangle(n,m)+1
        else:
            n=n-m
            return self.tilingRectangle(n,m)+1    

    # 20230609
    # https://leetcode.cn/problems/modify-graph-edge-weights/
    # https://leetcode.cn/problems/modify-graph-edge-weights/solution/liang-ci-dijkstran2-lgn-fu-za-du-by-free-qzu1/
    def modifiedGraphEdges(self, n: int, edges: List[List[int]], S: int, T: int, D: int) -> List[List[int]]:
        g = defaultdict(dict)
        book = dict()
        for i, (u, v, w) in enumerate(edges):
            if w > 0:
                g[u][v] = g[v][u] = w
            else:
                g[u][v] = g[v][u] = 1
                if u > v: u, v = v, u
                book[u, v] = i
        
        dist0 = [inf] * n
        dist0[S] = 0
        q = [(0, S)]
        while q:
            d, u = heappop(q)
            if d > dist0[u]: continue
            for v, w in g[u].items():
                nd = d + w
                if dist0[v] > nd:
                    dist0[v] = nd
                    heappush(q, (nd, v))
        
        if dist0[T] > D: return []
        need = D - dist0[T]
        
        dist1 = [inf] * n
        dist1[S] = 0
        q = [(0, S)]
        while q:
            d, u = heappop(q)
            if d > dist1[u]: continue
            for v, w in g[u].items():
                seg = tuple(sorted((u, v)))
                if seg in book:
                    z = dist0[v] + need - dist1[u]
                    i = book.pop(seg)
                    if w < z:
                        w = z
                    edges[i][2] = w
                nd = d + w
                if dist1[v] > nd:
                    dist1[v] = nd
                    heappush(q, (nd, v))
        if dist1[T] != D: return []
        
        for i in book.values():
            edges[i][2] = 2000000000
        
        return edges        

    # 20230610
    # https://leetcode.cn/problems/compare-strings-by-frequency-of-the-smallest-character/
    # https://leetcode.cn/problems/compare-strings-by-frequency-of-the-smallest-character/solution/er-fen-cha-zhao-pythonliang-xing-100-117-ou9x/
    def numSmallerByFrequency(self, queries: List[str], words: List[str]) -> List[int]:
        words, n = sorted(s.count(min(s)) for s in words), len(words)
        return [n - bisect.bisect(words, s.count(min(s))) for s in queries]       

    # 20230611
    # https://leetcode.cn/problems/remove-zero-sum-consecutive-nodes-from-linked-list/
    # https://leetcode.cn/problems/remove-zero-sum-consecutive-nodes-from-linked-list/solution/cpython3-1qian-zhui-he-wu-xu-zi-dian-by-3mcsv/
    def removeZeroSumSublists(self, head: ListNode) -> ListNode:
        presum_lastNode_dic = defaultdict(ListNode)
        
        dummy = ListNode(0)
        dummy.next = head
        p = dummy
        cur_presum = 0
        while p:
            cur_presum += p.val
            presum_lastNode_dic[cur_presum] = p #等于这去个前缀和的最右的一个结点
            p = p.next

        cur_presum = 0
        p = dummy
        while p:
            cur_presum += p.val
            p.next = presum_lastNode_dic[cur_presum].next   #2个结点中间，都直接删除了 要么就只有自己1个结点
            p = p.next
        
        return dummy.next    

    # 20230613
    # https://leetcode.cn/problems/number-of-unequal-triplets-in-array/
    # https://leetcode.cn/problems/number-of-unequal-triplets-in-array/solution/ha-xi-ji-shu-zu-he-ji-suan-pythonliang-x-rx52/
    def unequalTriplets(self, nums: List[int]) -> int:
        cnt = Counter(nums)
        return sum(cnt[x] * cnt[y] * cnt[z] for x, y, z in combinations(cnt, 3))

    # 20230614
    # https://leetcode.cn/problems/number-of-times-binary-string-is-prefix-aligned/
    # https://leetcode.cn/problems/number-of-times-binary-string-is-prefix-aligned/solution/qiao-miao-li-yong-xing-zhi-wei-hu-zui-da-79yx/
    def numTimesAllBlue(self, flips: List[int]) -> int:
        ans = mx = 0
        for i, x in enumerate(flips, 1):  # i 从 1 开始
            mx = max(mx, x)
            ans += mx == i
        return ans

    # 20230615
    # https://leetcode.cn/problems/can-make-palindrome-from-substring/
    # https://leetcode.cn/problems/can-make-palindrome-from-substring/solution/yi-bu-bu-you-hua-cong-qian-zhui-he-dao-q-yh5p/
    def canMakePaliQueries(self, s: str, queries: List[List[int]]) -> List[bool]:
        sum = [[0] * 26]
        for c in s:
            sum.append(sum[-1].copy())
            sum[-1][ord(c) - ord('a')] += 1

        ans = []
        for left, right, k in queries:
            m = 0
            for sl, sr in zip(sum[left], sum[right + 1]):
                m += (sr - sl) % 2  # 奇数+1，偶数+0
            ans.append(m // 2 <= k)
        return ans

    # 20230616
    # https://leetcode.cn/problems/parallel-courses-ii/
    # https://leetcode.cn/problems/parallel-courses-ii/solution/cpython3-gen-ju-ti-shi-zhuang-tai-ya-suo-jwx8/
    def minNumberOfSemesters(self, n: int, dependencies: List[List[int]], k: int) -> int:
        if len(dependencies) == 1:
            res = ceil(n / k)
            return 2 if res == 1 else res

        pre = [0 for _ in range(n)]
        for x, y in dependencies:
            x -= 1                  #归一化，从1开始->从开始
            y -= 1
            pre[y] |= (1 << x)      #n最多15位

        dp = [0 for _ in range(2 ** n)]
        for state in range(1, 2 ** n):
            dp[state] = n                   #注意不是state中1的个数。因为这只是中间的状态！！！
        for state in range(0, 2 ** n):      #枚举所有的状态
            #---- 找下一状态
            nxt = 0
            for x in range(n):
                if (state & pre[x]) == pre[x]:      #x前驱的课都已经修过了
                    nxt |= (1 << x)
            #---- 枚举nxt的子集
            nxt = nxt & (~ state)                   #state中已经为1，修过了。
            sub = nxt
            while sub > 0:
                bit1 = bin(sub)[2: ].count('1')
                # bit1 = self.cnt_bit1(sub)
                if bit1 <= k:
                    nxt_state = state | sub
                    dp[nxt_state] = min(dp[nxt_state], dp[state] + 1)    

                sub = (sub - 1) & nxt 

        return dp[2 ** n - 1]    

    # 20230617
    # https://leetcode.cn/problems/minimum-cuts-to-divide-a-circle/
    # https://leetcode.cn/problems/minimum-cuts-to-divide-a-circle/solution/python3javacgotypescript-yi-ti-yi-jie-fe-nhln/
    def numberOfCuts(self, n: int) -> int:
        return n if (n > 1 and n & 1) else n >> 1
        
    # 20230618
    # https://leetcode.cn/problems/number-of-closed-islands/
    # https://leetcode.cn/problems/number-of-closed-islands/solution/liang-chong-si-lu-xian-wai-hou-nei-chu-j-b1e4/
    def closedIsland(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])

        def dfs(x: int, y: int) -> None:
            grid[x][y] = 1  # 标记 (x,y) 被访问，避免重复访问
            # 访问四方向的 0
            for i, j in (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1):
                if 0 <= i < m and 0 <= j < n and grid[i][j] == 0:
                    dfs(i, j)

        for i in range(m):
            # 如果是第一行和最后一行，访问所有格子
            # 如果不是，只访问第一列和最后一列的格子
            step = 1 if i == 0 or i == m - 1 else n - 1
            for j in range(0, n, step):
                if grid[i][j] == 0:  # 从没有访问过的 0 出发
                    dfs(i, j)

        ans = 0
        for i in range(1, m - 1):
            for j in range(1, n - 1):
                if grid[i][j] == 0:  # 从没有访问过的 0 出发
                    ans += 1  # 一定是封闭岛屿
                    dfs(i, j)
        return ans

    # 20230619
    # https://leetcode.cn/problems/greatest-sum-divisible-by-three/
    # https://leetcode.cn/problems/greatest-sum-divisible-by-three/solution/ti-jie-5265-ke-bei-san-zheng-chu-de-zui-da-he-by-z/
    def maxSumDivThree(self, nums: List[int]) -> int:
        a = [x for x in nums if x % 3 == 0]
        b = sorted([x for x in nums if x % 3 == 1], reverse=True)
        c = sorted([x for x in nums if x % 3 == 2], reverse=True)

        ans = 0
        lb, lc = len(b), len(c)
        for j0 in [lb - 2, lb - 1, lb]:
            if j0 >= 0:
                for k0 in [lc - 2, lc - 1, lc]:
                    if k0 >= 0 and j0 % 3 == k0 % 3:
                        ans = max(ans, sum(b[:j0]) + sum(c[:k0]))
        return ans + sum(a)    

    # 20230620
    # https://leetcode.cn/problems/minimum-cost-to-connect-two-groups-of-points/
    # https://leetcode.cn/problems/minimum-cost-to-connect-two-groups-of-points/solution/python3javacgotypescript-yi-ti-yi-jie-zh-m0sn/
    def connectTwoGroups(self, cost: List[List[int]]) -> int:
        m, n = len(cost), len(cost[0])
        f = [[inf] * (1 << n) for _ in range(m + 1)]
        f[0][0] = 0
        for i in range(1, m + 1):
            for j in range(1 << n):
                for k in range(n):
                    if (j >> k & 1) == 0:
                        continue
                    c = cost[i - 1][k]
                    x = min(f[i][j ^ (1 << k)], f[i - 1][j], f[i - 1][j ^ (1 << k)]) + c
                    f[i][j] = min(f[i][j], x)
        return f[m][-1]    

    # 20230621
    # https://leetcode.cn/problems/fHi6rV/
    # https://leetcode.cn/problems/fHi6rV/solution/lei-si-qiu-dao-yu-ge-shu-dfsbian-li-fang-dtcr/
    def flipChess(self, board: List[str]) -> int:
        # 1 <= chessboard.length, chessboard[i].length <= 8
        # chessboard[i] 仅包含 "."、"O" 和 "X"
        m,n=len(board),len(board[0])
        ans = 0
        di = [[0,1],[0,-1],[-1,0],[1,0],[1,1],[1,-1],[-1,1],[-1,-1]]
        def dfs(i,j, dis):
            nonlocal res
            nonlocal ans
            for dix in dis:
                dx,dy = di[dix]
                xx,yy = i+dx,j+dy
                if 0<=xx<m and 0<=yy<n:
                    c = board0[xx][yy]
                    if c=='O':
                        ii=1
                        while 0<=(xx+ii*dx)<m and 0<=(yy+ii*dy)<n and board0[xx+ii*dx][yy+ii*dy]=='O':
                            ii+=1
                        if 0<=(xx+ii*dx)<m and 0<=(yy+ii*dy)<n and board0[xx+ii*dx][yy+ii*dy]=='X':
                            for iii in range(ii):
                                if board0[xx+iii*dx][yy+iii*dy] == 'O':
                                    res+=1
                                    ans = max(ans,res)
                                    board0[xx+iii*dx][yy+iii*dy] = 'X'
                                dfs(xx+iii*dx,yy+iii*dy, range(8))
                    if c=='X':
                        pass
        
        for i in range(m):
            for j in range(n):
                if board[i][j]=='.':
                    board0=[[board[i][j] for j in range(n)] for i in range(m)]
                    board0[i][j] = 'X'
                    res = 0
                    dfs(i,j, range(8))
        return ans    

    # 20230622
    # https://leetcode.cn/problems/pond-sizes-lcci/
    # https://leetcode.cn/problems/pond-sizes-lcci/solution/cpython3java-1bfsji-yi-hua-by-xinghe_xin-gbjl/
    def pondSizes(self, land: List[List[int]]) -> List[int]:
        dirs = [(-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1)]

        Row, Col = len(land), len(land[0])
        visited = [[False for _ in range(Col)] for _ in range(Row)]
        res = []
        q = collections.deque()
        for r in range(Row):
            for c in range(Col):
                if land[r][c] == 0 and visited[r][c] == False:
                    visited[r][c] = True
                    q.clear()
                    cnt = 0
                    q.append((r, c))
                    while q:
                        rr, cc = q.popleft()
                        cnt += 1
                        for dr, dc in dirs:
                            nr = rr + dr
                            nc = cc + dc
                            if 0 <= nr < Row and 0 <= nc < Col and land[nr][nc] == 0 and visited[nr][nc] == False:
                                visited[nr][nc] = True
                                q.append((nr, nc))
                    res.append(cnt)                

        res.sort()
        return res
    
    # 20230623
    # https://leetcode.cn/problems/maximum-value-of-a-string-in-an-array/
    # https://leetcode.cn/problems/maximum-value-of-a-string-in-an-array/solution/python-by-peng-han-lin-uhb9/
    def maximumValue(self, strs):
        length=[]
        for i in strs:
            flag=0
            for j in list(i):
                if j not in ['0','1','2','3','4','5','6','7','8','9']:
                    flag=1
            if flag==0:
                length.append(int(i,10))
            else:
                length.append(len(i))
        length.sort(reverse=True)
        return length[0]

    # 20230624
    # https://leetcode.cn/problems/maximize-grid-happiness/
    # https://leetcode.cn/problems/maximize-grid-happiness/solution/zhuang-ya-dfs-by-qin-qi-shu-hua-2-jga4/
    def getMaxGridHappiness(self, m: int, n: int, introvertsCount: int, extrovertsCount: int) -> int:
        @lru_cache(None)
        def dfs(i, state, state1, c, c1):
            if i == m * n:
                return 0
            if i >= n:
                res = dfs(i + 1, state >> 1, state1 >> 1, c, c1)
            else:
                res = dfs(i + 1, state, state1, c, c1)
            if c > 0:
                ans = 120
                if i % n > 0:
                    if state >> (min(i, n) - 1) & 1 == 1:
                        ans -= 60
                    elif state1 >> (min(i, n) - 1) & 1 == 1:
                        ans -= 10
                if i >= n:
                    if state & 1 == 1:
                        ans -= 60
                    elif state1 & 1 == 1:
                        ans -= 10
                if i >= n:
                    res = max(res, ans + dfs(i + 1, (state + 2 ** n) >> 1, state1 >> 1, c - 1, c1))
                else:
                    res = max(res, ans + dfs(i + 1, state + 2 ** i, state1, c - 1, c1))
            if c1 > 0:
                ans = 40
                if i % n > 0:
                    if state >> (min(i, n) - 1) & 1 == 1:
                        ans -= 10
                    elif state1 >> (min(i, n) - 1) & 1 == 1:
                        ans += 40
                if i >= n:
                    if state & 1 == 1:
                        ans -= 10
                    elif state1 & 1 == 1:
                        ans += 40
                if i >= n:
                    res = max(res, ans + dfs(i + 1, state >> 1, (state1 + 2 ** n) >> 1, c, c1 - 1))
                else:
                    res = max(res, ans + dfs(i + 1, state, state1 + 2 ** i, c, c1 - 1))

            return res
        return dfs(0, 0, 0, introvertsCount, extrovertsCount)

    # 20230625
    # https://leetcode.cn/problems/circle-and-rectangle-overlapping/
    # https://leetcode.cn/problems/circle-and-rectangle-overlapping/solution/hua-fen-qu-yu-python-3-by-z1m/
    def checkOverlap(self, radius: int, x_center: int, y_center: int, x1: int, y1: int, x2: int, y2: int) -> bool:
        # 条件 1：首先判断圆心是否在矩形内
        if x1 <= x_center <= x2 and y1 <= y_center <= y2:
            return True
        # 条件 2：圆心位于矩形的上下左右四个区域
        elif x_center > x2 and y1 <= y_center <= y2: # 右
            return radius >= x_center - x2
        elif y_center < y1 and x1 <= x_center <= x2: # 下
            return radius >= y1 - y_center
        elif x_center < x1 and y1<= y_center <= y2: # 左
            return radius >= x1 - x_center
        elif y_center > y2 and x1 <= x_center <= x2: # 上
            return radius >= y_center - y2
        else:
        # 条件 3：判断矩形的四个顶点是否在圆的内部
            return min((x1 - x_center) ** 2 + (y2 - y_center) ** 2,\
                       (x2 - x_center) ** 2 + (y2 - y_center) ** 2, \
                       (x2 - x_center) ** 2 + (y1 - y_center) ** 2, \
                       (x1 - x_center) ** 2 + (y1 - y_center) ** 2) <= radius ** 2

    # 20230626
    # https://leetcode.cn/problems/find-the-pivot-integer/
    # https://leetcode.cn/problems/find-the-pivot-integer/solution/python3javacgotypescript-yi-ti-shuang-ji-6qnq/
    def pivotInteger(self, n: int) -> int:
        for x in range(1, n + 1):
            if (1 + x) * x == (x + n) * (n - x + 1):
                return x
        return -1      
        
    # 20230627
    # https://leetcode.cn/problems/maximum-subarray-sum-with-one-deletion/
    # https://leetcode.cn/problems/maximum-subarray-sum-with-one-deletion/solution/cpython3java-1dp-by-xinghe_xinghe-d7gq/
    def maximumSum(self, arr: List[int]) -> int:
        n = len(arr)

        res = arr[0]
        dp = [[0, 0] for _ in range(n)]
        dp[0][0] = arr[0]
        for i in range(1, n):
            dp[i][0] = max(dp[i - 1][0], 0) + arr[i]
            dp[i][1] = max(dp[i - 1][1] + arr[i], dp[i - 1][0] + 0)
            res = max(res, dp[i][0], dp[i][1])

        return res     
        
    # 20230628
    # https://leetcode.cn/problems/minimum-incompatibility/
    # https://leetcode.cn/problems/minimum-incompatibility/solution/python3javacgotypescript-yi-ti-yi-jie-yu-us63/
    def minimumIncompatibility(self, nums: List[int], k: int) -> int:
        n = len(nums)
        m = n // k
        g = [-1] * (1 << n)
        for i in range(1, 1 << n):
            if i.bit_count() != m:
                continue
            s = set()
            mi, mx = 20, 0
            for j, x in enumerate(nums):
                if i >> j & 1:
                    if x in s:
                        break
                    s.add(x)
                    mi = min(mi, x)
                    mx = max(mx, x)
            if len(s) == m:
                g[i] = mx - mi
        f = [inf] * (1 << n)
        f[0] = 0
        for i in range(1 << n):
            if f[i] == inf:
                continue
            s = set()
            mask = 0
            for j, x in enumerate(nums):
                if (i >> j & 1) == 0 and x not in s:
                    s.add(x)
                    mask |= 1 << j
            if len(s) < m:
                continue
            j = mask
            while j:
                if g[j] != -1:
                    f[i | j] = min(f[i | j], f[i] + g[j])
                j = (j - 1) & mask
        return f[-1] if f[-1] != inf else -1
    
    # 20230629
    # https://leetcode.cn/problems/reconstruct-a-2-row-binary-matrix/
    # https://leetcode.cn/problems/reconstruct-a-2-row-binary-matrix/solution/python3javacgotypescript-yi-ti-yi-jie-ta-ecug/
    def reconstructMatrix(self, upper: int, lower: int, colsum: List[int]) -> List[List[int]]:
        n = len(colsum)
        ans = [[0] * n for _ in range(2)]
        for j, v in enumerate(colsum):
            if v == 2:
                ans[0][j] = ans[1][j] = 1
                upper, lower = upper - 1, lower - 1
            if v == 1:
                if upper > lower:
                    upper -= 1
                    ans[0][j] = 1
                else:
                    lower -= 1
                    ans[1][j] = 1
            if upper < 0 or lower < 0:
                return []
        return ans if lower == upper == 0 else []      

    # 20230630
    # https://leetcode.cn/problems/circular-sentence/
    # https://leetcode.cn/problems/circular-sentence/solution/python3javacgotypescript-yi-ti-shuang-ji-l68p/
    def isCircularSentence(self, s: str) -> bool:
        return s[0] == s[-1] and all(
            c != " " or s[i - 1] == s[i + 1] for i, c in enumerate(s)
        )          
        
    # 20230701
    # https://leetcode.cn/problems/two-sum/
    # https://leetcode.cn/problems/two-sum/solution/jian-dan-python-by-chanemo-muld/
    def twoSum(self, nums: List[int], target: int) -> List[int]:
         for i in range(len(nums)):
            # 计算需要找到的下一个目标数字
            res = target-nums[i]
                # 遍历剩下的元素，查找是否存在该数字
            if res in nums[i+1:]:
                # 若存在，返回答案。这里由于是两数之和，可采用.index()方法
                # 获得目标元素在nums[i+1:]这个子数组中的索引后，还需加上i+1才是该元素在nums中的索引
                return [i, nums[i+1:].index(res)+i+1]

    # 20230702
    # https://leetcode.cn/problems/add-two-numbers/
    # https://leetcode.cn/problems/add-two-numbers/solution/python3javacgo-yi-ti-yi-jie-mo-ni-jian-j-qdpm/
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        dummy = ListNode()
        carry, curr = 0, dummy
        while l1 or l2 or carry:
            s = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry
            carry, val = divmod(s, 10)
            curr.next = ListNode(val)
            curr = curr.next
            l1 = l1.next if l1 else None
            l2 = l2.next if l2 else None
        return dummy.next

    # 20230703
    # https://leetcode.cn/problems/add-two-numbers-ii/
    # https://leetcode.cn/problems/add-two-numbers-ii/solution/cpython3java-1mo-ni-by-xinghe_xinghe-r7p0/
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        nums1, nums2 = [], []
        while l1:
            nums1.append(l1.val)
            l1 = l1.next
        while l2:
            nums2.append(l2.val)
            l2 = l2.next
        
        n1, n2 = len(nums1), len(nums2)

        nums = []
        i, j = n1 - 1, n2 - 1
        add = 0
        while 0 <= i or 0 <= j or add > 0:
            cur = (nums1[i] if 0 <= i else 0) + (nums2[j] if 0 <= j else 0) + add
            nums.append(cur % 10)
            add = cur // 10
            i -= 1
            j -= 1

        dummy = ListNode()
        p = dummy
        for x in nums[::-1]:
            t = ListNode(x)
            p.next = t
            p = p.next
        return dummy.next

    # 20230704
    # https://leetcode.cn/problems/sum-in-a-matrix/description/
    # https://leetcode.cn/problems/sum-in-a-matrix/solutions/2329717/python3javacgotypescript-yi-ti-yi-jie-pa-jfl9/
    def matrixSum(self, nums: List[List[int]]) -> int:
        for row in nums:
            row.sort()
        return sum(map(max, zip(*nums)))     
        
    # 20230705
    # https://leetcode.cn/problems/k-items-with-the-maximum-sum/description/
    # https://leetcode.cn/problems/k-items-with-the-maximum-sum/solutions/2331148/python3javacgotypescript-yi-ti-yi-jie-ta-jo3r/
    def kItemsWithMaximumSum(self, numOnes: int, numZeros: int, numNegOnes: int, k: int) -> int:
        if numOnes >= k:
            return k
        if numZeros >= k - numOnes:
            return numOnes
        return numOnes - (k - numOnes - numZeros)       

    # 20230706
    # https://leetcode.cn/problems/maximum-split-of-positive-even-integers/description/
    # https://leetcode.cn/problems/maximum-split-of-positive-even-integers/solutions/2332558/python3javacgotypescript-yi-ti-yi-jie-ta-x9xg/
    def maximumEvenSplit(self, finalSum: int) -> List[int]:
        if finalSum & 1:
            return []
        cur = 2
        ans = []
        while (cur << 1) < finalSum:
            ans.append(cur)
            finalSum -= cur
            cur += 2
        ans.append(finalSum)
        return ans        
# 20230707
    # https://leetcode.cn/problems/time-to-cross-a-bridge/description/
    # https://leetcode.cn/problems/time-to-cross-a-bridge/solutions/2333977/python3javacgo-yi-ti-yi-jie-you-xian-dui-8p97/
    def findCrossingTime(self, n: int, k: int, time: List[List[int]]) -> int:
        time.sort(key=lambda x: x[0] + x[2])
        cur = 0
        wait_in_left, wait_in_right = [], []
        work_in_left, work_in_right = [], []
        for i in range(k):
            heappush(wait_in_left, -i)
        while 1:
            while work_in_left:
                t, i = work_in_left[0]
                if t > cur:
                    break
                heappop(work_in_left)
                heappush(wait_in_left, -i)
            while work_in_right:
                t, i = work_in_right[0]
                if t > cur:
                    break
                heappop(work_in_right)
                heappush(wait_in_right, -i)
            left_to_go = n > 0 and wait_in_left
            right_to_go = bool(wait_in_right)
            if not left_to_go and not right_to_go:
                nxt = inf
                if work_in_left:
                    nxt = min(nxt, work_in_left[0][0])
                if work_in_right:
                    nxt = min(nxt, work_in_right[0][0])
                cur = nxt
                continue
            if right_to_go:
                i = -heappop(wait_in_right)
                cur += time[i][2]
                if n == 0 and not wait_in_right and not work_in_right:
                    return cur
                heappush(work_in_left, (cur + time[i][3], i))
            else:
                i = -heappop(wait_in_left)
                cur += time[i][0]
                n -= 1
                heappush(work_in_right, (cur + time[i][1], i))    

    # 20230708
    # https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/description/
    # https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/solutions/2335244/python3javacgotypescript-yi-ti-shuang-ji-a8kx/
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        n = len(numbers)
        for i in range(n - 1):
            x = target - numbers[i]
            j = bisect_left(numbers, x, lo=i + 1)
            if j < n and numbers[j] == x:
                return [i + 1, j + 1]

    # 20230709
    # https://leetcode.cn/problems/3sum/description/
    # https://leetcode.cn/problems/3sum/solutions/39722/pai-xu-shuang-zhi-zhen-zhu-xing-jie-shi-python3-by/
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        n=len(nums)
        res=[]
        if(not nums or n<3):
            return []
        nums.sort()
        res=[]
        for i in range(n):
            if(nums[i]>0):
                return res
            if(i>0 and nums[i]==nums[i-1]):
                continue
            L=i+1
            R=n-1
            while(L<R):
                if(nums[i]+nums[L]+nums[R]==0):
                    res.append([nums[i],nums[L],nums[R]])
                    while(L<R and nums[L]==nums[L+1]):
                        L=L+1
                    while(L<R and nums[R]==nums[R-1]):
                        R=R-1
                    L=L+1
                    R=R-1
                elif(nums[i]+nums[L]+nums[R]>0):
                    R=R-1
                else:
                    L=L+1
        return res

    # 20230710
    # https://leetcode.cn/problems/3sum-closest/description/
    # https://leetcode.cn/problems/3sum-closest/solutions/2337775/python3gobao-li-mo-ni-shuang-zhi-zhen-pa-iioh/
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        nums.sort()  # 先对数组进行排序
        closest_sum = float('inf')  # 用于记录最接近目标值的三数之和
        min_diff = float('inf')  # 用于记录当前最小的差值

        for i in range(len(nums) - 2):
            left = i + 1
            right = len(nums) - 1

            while left < right:
                sum3 = nums[i] + nums[left] + nums[right]
                diff = abs(sum3 - target)

                if diff < min_diff:  # 如果当前差值更小，则更新最小差值和对应的三数之和
                    min_diff = diff
                    closest_sum = sum3

                if sum3 == target:  # 如果找到与目标值完全相等的三数之和，则直接返回
                    return sum3
                elif sum3 < target:  # 如果三数之和小于目标值，移动左指针增大和
                    left += 1
                else:  # 如果三数之和大于目标值，移动右指针减小和
                    right -= 1

        return closest_sum     

    # 20230711
    # https://leetcode.cn/problems/maximum-alternating-subsequence-sum/description/
    # https://leetcode.cn/problems/maximum-alternating-subsequence-sum/solutions/2339030/python3javacgotypescript-yi-ti-yi-jie-do-22yp/
    def maxAlternatingSum(self, nums: List[int]) -> int:
        n = len(nums)
        f = [0] * (n + 1)
        g = [0] * (n + 1)
        for i, x in enumerate(nums, 1):
            f[i] = max(g[i - 1] - x, f[i - 1])
            g[i] = max(f[i - 1] + x, g[i - 1])
        return max(f[n], g[n])
    
    # 20230712
    # https://leetcode.cn/problems/alternating-digit-sum/description/
    # https://leetcode.cn/problems/alternating-digit-sum/solutions/2340417/python3gopython3yi-xing-zhi-jie-mo-ni-by-hdfr/
    def alternateDigitSum(self, n: int) -> int:
        digit_sum = 0
        for i, s in enumerate(str(n)):
            if i % 2 == 0:
                digit_sum += int(s)
            else:
                digit_sum -= int(s)
        return digit_sum        
        
    # 20230713
    # https://leetcode.cn/problems/minimum-falling-path-sum/
    # https://leetcode.cn/problems/minimum-falling-path-sum/solutions/2341854/letmefly-931xia-jiang-lu-jing-zui-xiao-h-ier1/
    def minFallingPathSum(self, matrix: List[List[int]]) -> int:
        n = len(matrix)
        for i in range(1, n):
            for j in range(n):
                m = 1e8
                for k in range(-1, 2):
                    if 0 <= j + k < n:
                        m = min(m, matrix[i - 1][j + k])
                matrix[i][j] += m
        return min(matrix[-1])
    
    
    # 20230714
    # https://leetcode.cn/problems/distribute-coins-in-binary-tree/description/
    # https://leetcode.cn/problems/distribute-coins-in-binary-tree/solutions/1682559/sui-bian-zuo-by-qian-li-ma-8-fipb/
    def distributeCoins(self, root: Optional[TreeNode]) -> int:
        ans=0
        if not root.left and not root.right:
            return 0
        ans=0
        def dfs(root,parent):
            nonlocal ans 
            if not root.left and not root.right:
                ans+=abs(root.val-1)
                
                return root.val-1
            else:
                if root.left:
                    root.val+=dfs(root.left,root)
                if root.right:
                    root.val+=dfs(root.right,root)
                if root.val!=1:
                    ans+=abs(root.val-1)
                return root.val-1
        dfs(root,None)
        return ans        

    # 20230715
    # https://leetcode.cn/problems/4sum/description/
    # https://leetcode.cn/problems/4sum/solutions/2344514/ji-zhi-you-hua-ji-yu-san-shu-zhi-he-de-z-1f0b/
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()
        ans = []
        n = len(nums)
        for a in range(n - 3):  # 枚举第一个数
            x = nums[a]
            if a and x == nums[a - 1]:  # 跳过重复数字
                continue
            if x + nums[a + 1] + nums[a + 2] + nums[a + 3] > target:  # 优化一
                break
            if x + nums[-3] + nums[-2] + nums[-1] < target:  # 优化二
                continue
            for b in range(a + 1, n - 2):  # 枚举第二个数
                y = nums[b]
                if b > a + 1 and y == nums[b - 1]:  # 跳过重复数字
                    continue
                if x + y + nums[b + 1] + nums[b + 2] > target:  # 优化一
                    break
                if x + y + nums[-2] + nums[-1] < target:  # 优化二
                    continue
                # 双指针枚举第三个数和第四个数
                c = b + 1
                d = n - 1
                while c < d:
                    s = x + y + nums[c] + nums[d]  # 四数之和
                    if s > target:
                        d -= 1
                    elif s < target:
                        c += 1
                    else:  # s == target
                        ans.append([x, y, nums[c], nums[d]])
                        c += 1
                        while c < d and nums[c] == nums[c - 1]:  # 跳过重复数字
                            c += 1
                        d -= 1
                        while d > c and nums[d] == nums[d + 1]:  # 跳过重复数字
                            d -= 1
        return ans
    
    # 20230716
    # https://leetcode.cn/problems/sum-of-distances-in-tree/description/
    # https://leetcode.cn/problems/sum-of-distances-in-tree/solutions/1767474/er-xu-cheng-ming-jiu-xu-zui-python3shu-x-hub3/
    def sumOfDistancesInTree(
            self, n: int, edges: List[List[int]]) -> List[int]:
        undirected = [[] for i in range(n)]
        for i, j in edges:
            undirected[i].append(j)
            undirected[j].append(i)

        directed = [[] for _ in range(n)]
        visit = [0] * n
        visit[0] = 1
        stack = [0]
        while stack:
            i = stack.pop(0)
            for j in undirected[i]:
                if not visit[j]:
                    visit[j] = 1
                    directed[i].append(j)
                    stack.append(j)

        def son_dfs(i):
            dis = 0
            cnt = 0
            for j in directed[i]:
                x, y = son_dfs(j)
                cnt += y
                dis += x + y
            son_dis[i] = dis
            son_cnt[i] = cnt
            return dis, cnt + 1

        def father_dfs(i):
            for j in directed[i]:
                father_dis[j] += father_dis[i] + n - 1 - son_cnt[j] # 父节点从上往下传递的
                father_dis[j] += son_dis[i] - son_dis[j] - 1 - son_cnt[j] # 同一个父节点的其他子节点距离
                father_dfs(j)
            return

        son_dis = [0] * n
        son_cnt = [0] * n
        father_dis = [0] * n
        son_dfs(0)
        father_dfs(0)
        ans = [son_dis[i] + father_dis[i] for i in range(n)]
        return ans     

    # 20230717
    # https://leetcode.cn/problems/add-strings/description/
    # https://leetcode.cn/problems/add-strings/solutions/12250/add-strings-shuang-zhi-zhen-fa-by-jyd/
    def addStrings(self, num1: str, num2: str) -> str:
        res = ""
        i, j, carry = len(num1) - 1, len(num2) - 1, 0
        while i >= 0 or j >= 0:
            n1 = int(num1[i]) if i >= 0 else 0
            n2 = int(num2[j]) if j >= 0 else 0
            tmp = n1 + n2 + carry
            carry = tmp // 10
            res = str(tmp % 10) + res
            i, j = i - 1, j - 1
        return "1" + res if carry else res

    # 20230718
    # https://leetcode.cn/problems/minimum-interval-to-include-each-query/description/
    # https://leetcode.cn/problems/minimum-interval-to-include-each-query/solutions/1776477/by-v5qyy4q65w-mpf3/
    def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]:

        res = [-1]*len(queries) #答案初始化为全-1
        qs = [[queries[j],j] for j in range(len(queries))] #排序时把查询所在下标带上，否则就不知道是哪个查询了
        qs.sort()

        intervals.sort() #区间按左端点排序，默认关键字就是
        pi = 0 
        hea = []

        for q,j in qs:
            while pi<len(intervals) and intervals[pi][0]<=q: #左端点不超过q的都进堆
                heapq.heappush(hea,[intervals[pi][1]-intervals[pi][0]+1,intervals[pi][1]]) #进堆时把区间长度和右端点同时进去，前者是关键字，后者用来判断是否是垃圾数据
                pi+=1
            while hea and hea[0][1]<q: #堆顶右端点小于q说明是“垃圾数据”，扔掉
                heapq.heappop(hea)
            if hea: 
                res[j]=hea[0][0] #如果堆非空，说明这个查询能找到有效区间，长度最短的一个就是答案
        
        return res

    # 20230719
    # https://leetcode.cn/problems/walking-robot-simulation/description/
    # https://leetcode.cn/problems/walking-robot-simulation/solutions/2349627/python3goji-he-mo-ni-xiang-xi-zhu-shi-by-eb7k/
    def robotSim(self, commands: List[int], obstacles: List[List[int]]) -> int:
        # 定义四个方向的向量，依次为北、东、南、西
        dx = [0, 1, 0, -1]
        dy = [1, 0, -1, 0]
        # 初始化机器人的位置和面向的方向
        x, y, direction = 0, 0, 0
        # 将障碍物的坐标转换为集合，方便查找
        obstacles = set(tuple(obstacle) for obstacle in obstacles)
        max_distance_squared = 0

        for command in commands:
            # 左转
            if command == -2:
                direction = (direction - 1) % 4
            # 右转
            elif command == -1:
                direction = (direction + 1) % 4
            # 前进
            else:
                # 循环模拟每一步前进
                for _ in range(command):
                    next_x, next_y = x + dx[direction], y + dy[direction]
                    # 如果下一步的坐标是障碍物，则停在障碍物前一格
                    if (next_x, next_y) in obstacles:
                        break
                    # 否则继续前进
                    x, y = next_x, next_y
                    max_distance_squared = max(max_distance_squared, x*x + y*y)

        return max_distance_squared
    
    # 20230720
    # https://leetcode.cn/problems/maximum-sum-circular-subarray/description/
    # https://leetcode.cn/problems/maximum-sum-circular-subarray/solutions/2351107/mei-you-si-lu-yi-zhang-tu-miao-dong-pyth-ilqh/
    def maxSubarraySumCircular(self, nums: List[int]) -> int:
        max_s = -inf  # 最大子数组和，不能为空
        min_s = 0     # 最小子数组和，可以为空
        max_f = min_f = 0
        for x in nums:
            # 以 nums[i-1] 结尾的子数组选或不选（取 max）+ x = 以 x 结尾的最大子数组和
            max_f = max(max_f, 0) + x
            max_s = max(max_s, max_f)
            # 以 nums[i-1] 结尾的子数组选或不选（取 min）+ x = 以 x 结尾的最小子数组和
            min_f = min(min_f, 0) + x
            min_s = min(min_s, min_f)
        if sum(nums) == min_s:
            return max_s
        return max(max_s, sum(nums) - min_s)     
        
    # 20230721
    # https://leetcode.cn/problems/max-value-of-equation/description/
    # https://leetcode.cn/problems/max-value-of-equation/solutions/2352457/on-dan-diao-dui-lie-fu-ti-dan-pythonjava-hhrr/
    def findMaxValueOfEquation(self, points: List[List[int]], k: int) -> int:
        ans = -inf
        q = deque()
        for x, y in points:
            while q and q[0][0] < x - k:  # 队首超出范围
                q.popleft()  # 弹它！
            if q:
                ans = max(ans, x + y + q[0][1])  # 加上最大的 yi-xi
            while q and q[-1][1] <= y - x:  # 队尾不如新来的强
                q.pop()  # 弹它！
            q.append((x, y - x))
        return ans          

    # 20230722
    # https://leetcode.cn/problems/lemonade-change/description/
    # https://leetcode.cn/problems/lemonade-change/solutions/2353728/python3javacgorust-yi-ti-yi-jie-tan-xin-u69kg/
    def lemonadeChange(self, bills: List[int]) -> bool:
        five = ten = 0
        for v in bills:
            if v == 5:
                five += 1
            elif v == 10:
                ten += 1
                five -= 1
            else:
                if ten:
                    ten -= 1
                    five -= 1
                else:
                    five -= 3
            if five < 0:
                return False
        return True

    # 20230723
    # https://leetcode.cn/problems/trapping-rain-water/description/
    # https://leetcode.cn/problems/trapping-rain-water/solutions/2354988/python3javacgotypescript-yi-ti-yi-jie-do-xc9z/
    def trap(self, height: List[int]) -> int:
        n = len(height)
        left = [height[0]] * n
        right = [height[-1]] * n
        for i in range(1, n):
            left[i] = max(left[i - 1], height[i])
            right[n - i - 1] = max(right[n - i], height[n - i - 1])
        return sum(min(l, r) - h for l, r, h in zip(left, right, height))
    
    # 20230724
    # https://leetcode.cn/problems/jewels-and-stones/description/
    # https://leetcode.cn/problems/jewels-and-stones/solutions/2356253/ben-ti-zui-you-jie-xian-xing-shi-jian-ch-ddw3/
    def numJewelsInStones(self, jewels: str, stones: str) -> int:
        # 把 jewels 转换成字符集合 mask
        mask = 0
        for c in jewels:
            mask |= 1 << (ord(c) & 63)
        # 统计有多少 stones[i] 在集合 mask 中
        return sum(mask >> (ord(c) & 63) & 1 for c in stones)
    
    # 20230725
    # https://leetcode.cn/problems/minimum-operations-to-halve-array-sum/description/
    # https://leetcode.cn/problems/minimum-operations-to-halve-array-sum/solutions/2357693/python3javacgotypescript-yi-ti-yi-jie-ta-51nu/
    def halveArray(self, nums: List[int]) -> int:
        s = sum(nums) / 2
        h = []
        for v in nums:
            heappush(h, -v)
        ans = 0
        while s > 0:
            t = -heappop(h) / 2
            s -= t
            heappush(h, -t)
            ans += 1
        return ans
    
    # 20230726
    # https://leetcode.cn/problems/handling-sum-queries-after-update/
    # https://leetcode.cn/problems/handling-sum-queries-after-update/solutions/2359102/wei-yun-suan-jin-xing-mo-ni-chu-li-pytho-rcci/
    def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:
        ans, len1, sum2, s1 = [], sum(nums1), sum(nums2), ''.join(str(x) for x in nums1)
        for q in queries:
            if   q[0] == 1:                
                len1 += (l_ := q[2] - q[1] + 1) - (s1[q[1]: q[2] + 1].count('1') << 1)
                s1 = s1[:q[1]] + bin(int(s1[q[1]: q[2] + 1], 2) ^ int('1' * l_, 2))[2:].zfill(l_) + s1[q[2] + 1 :]
            elif q[0] == 2:
                sum2 += q[1] * len1
            elif q[0] == 3:
                ans.append(sum2)
        return ans        

    # 20230727
    # https://leetcode.cn/problems/delete-greatest-value-in-each-row/description/
    # https://leetcode.cn/problems/delete-greatest-value-in-each-row/solutions/2360658/python3javacgorust-yi-ti-yi-jie-pai-xu-b-tgzp/
    def deleteGreatestValue(self, grid: List[List[int]]) -> int:
        for row in grid:
            row.sort()
        return sum(max(col) for col in zip(*grid))

    # 20230728
    # https://leetcode.cn/problems/parallel-courses-iii/description/
    # https://leetcode.cn/problems/parallel-courses-iii/solutions/2362050/python3gotuo-bu-pai-xu-suan-fa-xiang-xi-hyghp/
    def minimumTime(self, n: int, relations: List[List[int]], time: List[int]) -> int:
        graph = defaultdict(list)
        in_degree = [0] * (n + 1)
        complete_time = [0] * (n + 1)
        
        for prev, next in relations:
            graph[prev].append(next)
            in_degree[next] += 1
            
        queue = deque()
        for i in range(1, n + 1):
            if in_degree[i] == 0:
                queue.append(i)
                complete_time[i] = time[i - 1]
        
        while queue:
            current = queue.popleft()
            for next in graph[current]:
                in_degree[next] -= 1
                complete_time[next] = max(complete_time[next], complete_time[current] + time[next - 1])
                if in_degree[next] == 0:
                    queue.append(next)
        
        return max(complete_time)

    # 20230729
    # https://leetcode.cn/problems/linked-list-cycle/
    # https://leetcode.cn/problems/linked-list-cycle/solutions/2363390/python3javacgotypescript-yi-ti-yi-jie-ku-ypvu/
    def hasCycle(self, head: ListNode) -> bool:
        slow = fast = head
        while fast and fast.next:
            slow, fast = slow.next, fast.next.next
            if slow == fast:
                return True
        return False

    # 20230730
    # https://leetcode.cn/problems/linked-list-cycle-ii/
    # https://leetcode.cn/problems/linked-list-cycle-ii/solutions/2364387/python3javacgotypescript-yi-ti-yi-jie-ku-ykx0/
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        fast = slow = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                ans = head
                while ans != slow:
                    ans = ans.next
                    slow = slow.next
                return ans

    # 20230731
    # https://leetcode.cn/problems/reorder-list/description/
    # https://leetcode.cn/problems/reorder-list/solutions/2365667/python3javacgo-yi-ti-yi-jie-kuai-man-zhi-t9u2/
    def reorderList(self, head: Optional[ListNode]) -> None:
        # 快慢指针找到链表中点
        fast = slow = head
        while fast.next and fast.next.next:
            slow = slow.next
            fast = fast.next.next
        
        # cur 指向右半部分链表
        cur = slow.next
        slow.next = None
        
        # 反转右半部分链表
        pre = None
        while cur:
            t = cur.next
            cur.next = pre
            pre, cur = cur, t
        cur = head

        # 此时 cur, pre 分别指向链表左右两半的第一个节点
        # 合并
        while pre:
            t = pre.next
            pre.next = cur.next
            cur.next = pre
            cur, pre = pre.next, t

    # 20230801
    # https://leetcode.cn/problems/power-of-heroes/description/
    # https://leetcode.cn/problems/power-of-heroes/solutions/2268792/gong-xian-fa-pythonjavacgo-by-endlessche-d4jx/
    def sumOfPower(self, nums: List[int]) -> int:
        MOD = 10 ** 9 + 7
        nums.sort()
        ans = s = 0
        for x in nums:  # x 作为最大值
            ans = (ans + x * x * (x + s)) % MOD
            s = (s * 2 + x) % MOD  # 递推计算下一个 s
        return ans

    # 20230802
    # https://leetcode.cn/problems/card-flipping-game/description/
    # https://leetcode.cn/problems/card-flipping-game/solutions/2368863/yue-du-li-jie-ti-pythonjavacgojs-by-endl-ze7f/
    def flipgame(self, fronts: List[int], backs: List[int]) -> int:
        forbidden = {x for x, y in zip(fronts, backs) if x == y}
        return min((x for x in fronts + backs if x not in forbidden), default=0)

    # 20230803
    # https://leetcode.cn/problems/remove-comments/description/
    # https://leetcode.cn/problems/remove-comments/solutions/2370636/python3javacgorust-yi-ti-yi-jie-fen-qing-0vka/
    def removeComments(self, source: List[str]) -> List[str]:
        ans = []
        t = []
        block_comment = False
        for s in source:
            i, m = 0, len(s)
            while i < m:
                if block_comment:
                    if i + 1 < m and s[i : i + 2] == "*/":
                        block_comment = False
                        i += 1
                else:
                    if i + 1 < m and s[i : i + 2] == "/*":
                        block_comment = True
                        i += 1
                    elif i + 1 < m and s[i : i + 2] == "//":
                        break
                    else:
                        t.append(s[i])
                i += 1
            if not block_comment and t:
                ans.append("".join(t))
                t.clear()
        return ans

    # 20230804
    # https://leetcode.cn/problems/unique-paths-iii/
    # https://leetcode.cn/problems/unique-paths-iii/submissions/
    def uniquePathsIII(self, grid: List[List[int]]) -> int:
        def dfs(i, j, c):#分别是横纵坐标和步数
            if grid[i][j] == 2:#如果是终点
                return int(c == k + 1)#返回步数是否等于无障碍方格+终点数量
            res = 0
            for x, y in [[i + 1, j], [i - 1, j], [i, j + 1], [i, j - 1]]:
                if 0 <= x < m and 0 <= y < n and grid[x][y] != -1 and visit[x][y] == 0:
                    visit[x][y] = 1#回溯的经典场景,dfs之前标记已遍历,dfs之后标记未遍历
                    res += dfs(x, y, c + 1)
                    visit[x][y] = 0#标记未遍历
            return res
        m, n = len(grid), len(grid[0])
        visit = [[0 for i in range(n)] for j in range(m)]#和网格大小相同,用于标记方格是否被通过
        k = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 1:#记录起点
                    s = (i, j)
                elif grid[i][j] == 0:#对无障碍方格计数
                    k += 1
        visit[s[0]][s[1]] = 1#起点开始,所以已经遍历
        return dfs(s[0], s[1], 0)

    # 20230805
    # https://leetcode.cn/problems/merge-two-sorted-lists/description/
    # https://leetcode.cn/problems/merge-two-sorted-lists/solutions/103891/yi-kan-jiu-hui-yi-xie-jiu-fei-xiang-jie-di-gui-by-/
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1: return l2  # 终止条件，直到两个链表都空
        if not l2: return l1
        if l1.val <= l2.val:  # 递归调用
            l1.next = self.mergeTwoLists(l1.next,l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1,l2.next)
            return l2

    # 20230806
    # https://leetcode.cn/problems/swap-nodes-in-pairs/
    # https://leetcode.cn/problems/swap-nodes-in-pairs/solutions/2374872/tu-jie-die-dai-di-gui-yi-zhang-tu-miao-d-51ap/
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head is None or head.next is None:  # 递归边界
            return head  # 不足两个节点，无需交换

        node1 = head
        node2 = head.next
        node3 = node2.next

        node1.next = self.swapPairs(node3)  # 1 指向递归返回的链表头
        node2.next = node1  # 2 指向 1

        return node2  # 返回交换后的链表头节点

    # 20230807
    # https://leetcode.cn/problems/reverse-string/description/
    # https://leetcode.cn/problems/reverse-string/solutions/2376290/ji-chong-bu-tong-de-xie-fa-pythonjavacgo-9trb/
    def reverseString(self, s: List[str]) -> None:
        for i in range(len(s) // 2):
            s[i], s[-i - 1] = s[-i - 1], s[i]

    # 20230808
    # https://leetcode.cn/problems/maximum-absolute-sum-of-any-subarray/description/
    # https://leetcode.cn/problems/maximum-absolute-sum-of-any-subarray/solutions/2377930/liang-chong-fang-fa-dong-tai-gui-hua-qia-dczr/
    def maxAbsoluteSum(self, nums: List[int]) -> int:
        ans = f_max = f_min = 0
        for x in nums:
            f_max = max(f_max, 0) + x
            f_min = min(f_min, 0) + x
            ans = max(ans, f_max, -f_min)
        return ans

    # 20230809
    # https://leetcode.cn/problems/subtract-the-product-and-sum-of-digits-of-an-integer/
    # https://leetcode.cn/problems/subtract-the-product-and-sum-of-digits-of-an-integer/solutions/2379550/python3javacgorust-yi-ti-yi-jie-mo-ni-by-jud8/
    def subtractProductAndSum(self, n: int) -> int:
        x, y = 1, 0
        while n:
            n, v = divmod(n, 10)
            x *= v
            y += v
        return x - y

    # 20230810
    # https://leetcode.cn/problems/minimum-falling-path-sum-ii/description/
    # https://leetcode.cn/problems/minimum-falling-path-sum-ii/solutions/2381174/python3javacgotypescript-yi-ti-yi-jie-do-sko0/
    def minFallingPathSum(self, grid: List[List[int]]) -> int:
        n = len(grid)
        f = [[0] * n for _ in range(n + 1)]
        for i, row in enumerate(grid, 1):
            for j, v in enumerate(row):
                x = min((f[i - 1][k] for k in range(n) if k != j), default=0)
                f[i][j] = v + x
        return min(f[n])

    # 20230811
    # https://leetcode.cn/problems/matrix-diagonal-sum/description/
    # https://leetcode.cn/problems/matrix-diagonal-sum/solutions/2382818/python3javacgotypescript-yi-ti-yi-jie-zh-q3vk/
    def diagonalSum(self, mat: List[List[int]]) -> int:
        ans = 0
        n = len(mat)
        for i, row in enumerate(mat):
            j = n - i - 1
            ans += row[i] + (0 if j == i else row[j])
        return ans

    # 20230812
    # https://leetcode.cn/problems/merge-k-sorted-lists/
    # https://leetcode.cn/problems/merge-k-sorted-lists/solutions/2384787/quan-chai-jie-fang-fa-su-du-bing-bu-man-aos7b/
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        if not nodelist:return None
        list_native = []
        for node in nodelist:
            
            while node:
                list_native.append(node.val)
                node = node.next
        list_native.sort()
        head = ListNode()
        move = head
        for i in list_native:
            move.next = ListNode(i)
            move = move.next
        return head.next

    # 20230813
    # https://leetcode.cn/problems/merge-sorted-array/description/
    # https://leetcode.cn/problems/merge-sorted-array/solutions/2385610/dao-xu-shuang-zhi-zhen-wei-shi-yao-dao-x-xxkp/
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        p1, p2, p = m - 1, n - 1, m + n - 1
        while p2 >= 0:  # nums2 还有要合并的元素
            # 如果 p1 < 0，那么走 else 分支，把 nums2 合并到 nums1 中
            if p1 >= 0 and nums1[p1] > nums2[p2]:
                nums1[p] = nums1[p1]  # 填入 nums1[p1]
                p1 -= 1
            else:
                nums1[p] = nums2[p2]  # 填入 nums2[p1]
                p2 -= 1
            p -= 1  # 下一个要填入的位置

    # 20230814
    # https://leetcode.cn/problems/merge-two-binary-trees/description/
    # https://leetcode.cn/problems/merge-two-binary-trees/solutions/2387255/kan-dao-di-gui-jiu-yun-dai-ni-li-jie-di-leixm/
    def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if root1 is None: return root2
        if root2 is None: return root1
        return TreeNode(root1.val + root2.val,
            self.mergeTrees(root1.left, root2.left),    # 合并左子树
            self.mergeTrees(root1.right, root2.right))  # 合并右子树
    
    # 20230815
    # https://leetcode.cn/problems/find-and-replace-in-string/description/
    # https://leetcode.cn/problems/find-and-replace-in-string/solutions/2388853/xian-xing-zuo-fa-pythonjavacgojs-by-endl-uofo/
    def findReplaceString(self, s: str, indices: List[int], sources: List[str], targets: List[str]) -> str:
        replace = [(c, 1) for c in s]
        for i, src, tar in zip(indices, sources, targets):
            if s.startswith(src, i):  # 判断 s[i:] 的前缀是否为 src，这样写无需切片
                replace[i] = (tar, len(src))  # (替换后的字符串，被替换的长度)

        ans = []
        i = 0
        while i < len(s):
            ans.append(replace[i][0])  # 替换后的字符串（默认为 s[i]）
            i += replace[i][1]  # 被替换的长度（默认为 1）
        return ''.join(ans)

    # 20230816
    # https://leetcode.cn/problems/find-the-losers-of-the-circular-game/description/
    # https://leetcode.cn/problems/find-the-losers-of-the-circular-game/solutions/2390555/python3javacgorust-yi-ti-yi-jie-mo-ni-by-qd98/
    def circularGameLosers(self, n: int, k: int) -> List[int]:
        vis = [False] * n
        i, p = 0, 1
        while not vis[i]:
            vis[i] = True
            i = (i + p * k) % n
            p += 1
        return [i + 1 for i in range(n) if not vis[i]]

    # 20230817
    # https://leetcode.cn/problems/number-of-ways-of-cutting-a-pizza/description/
    # https://leetcode.cn/problems/number-of-ways-of-cutting-a-pizza/solutions/241055/ji-yi-hua-di-gui-bu-xu-yao-guan-dpci-xu-rong-yi-xi/
    def ways(self, pizza, k: int) -> int:
        mod=int(1e9+7)
        row,col=len(pizza),len(pizza[0])
        DP=[{(row,col):(0,0)} for i in range(k)] 
        _FA=[[None]*col for i in range(row)]
        def FA(r,c): return (row,col) if r>=row or c>=col else _FA[r][c] 
        for r in range(row)[::-1]:     
            for c in range(col)[::-1]:   
                _FA[r][c] = (r,c) if 'A'==pizza[r][c] else tuple(map(min,zip(FA(r+1,c),FA(r,c+1)) ))
                if _FA[r][c]!=(row,col):
                    DP[0][_FA[r][c]] = (1,) 
        def dfs(r,c,rm):    
            if (r, c) in DP[rm]: return DP[rm][r, c]  
            nr, nc = FA(r+1,c)
            a0 = (dfs(nr,nc,rm)[0]+(nr-r)*sum(dfs(nr,nc,rm-1)))%mod
            nr, nc = FA(r,c+1)
            a1= (dfs(nr,nc,rm)[1]+(nc-c)*sum(dfs(nr,nc,rm-1)))%mod
            DP[rm][r, c]=(a0,a1)
            return (a0,a1)
        r0,c0=FA(0,0)
        return sum(dfs(r0,c0,k-1)) 

    # 20230818
    # https://leetcode.cn/problems/pizza-with-3n-slices/description/
    # https://leetcode.cn/problems/pizza-with-3n-slices/solutions/2393598/python3dong-tai-gui-hua-by-fdvoid0-149o/
    def maxSizeSlices(self, slices: List[int]) -> int:
        n = len(slices)
        k = n // 3
        
        def maxSumSlices(slices):
            m = len(slices)
            dp = [[0] * (k + 1) for _ in range(m + 1)]
            
            for i in range(1, m + 1):
                for j in range(1, k + 1):
                    dp[i][j] = max(dp[i-1][j], dp[i-2][j-1] + slices[i-1])
            
            return dp[m][k]
        
        # 情况1: 选择第一个切片，排除最后一个切片
        max1 = maxSumSlices(slices[:-1])
        
        # 情况2: 排除第一个切片，考虑所有切片
        max2 = maxSumSlices(slices[1:])
        
        return max(max1, max2)

    # 20230819
    # https://leetcode.cn/problems/add-two-integers/description/
    # https://leetcode.cn/problems/add-two-integers/solutions/2394899/python3javacgorust-yi-ti-shuang-jie-shi-k0rbl/
    def sum(self, num1: int, num2: int) -> int:
        return num1 + num2

    # 20230820
    # https://leetcode.cn/problems/root-equals-sum-of-children/description/
    # https://leetcode.cn/problems/root-equals-sum-of-children/solutions/2396504/python3javacgotypescript-yi-ti-yi-jie-zh-zpv3/
    def checkTree(self, root: Optional[TreeNode]) -> bool:
        return root.val == root.left.val + root.right.val

    # 20230821
    # https://leetcode.cn/problems/move-pieces-to-obtain-a-string/description/
    # https://leetcode.cn/problems/move-pieces-to-obtain-a-string/solutions/1658923/nao-jin-ji-zhuan-wan-pythonjavacgo-by-en-9sqt/
    def canChange(self, start: str, target: str) -> bool:
        if start.replace('_', '') != target.replace('_', ''):
            return False
        j = 0
        for i, c in enumerate(start):
            if c == '_': continue
            while target[j] == '_': 
                j += 1
            if i != j and (c == 'L') == (i < j):
                return False
            j += 1
        return True

    # 20230822
    # https://leetcode.cn/problems/maximize-distance-to-closest-person/description/
    # https://leetcode.cn/problems/maximize-distance-to-closest-person/solutions/2399132/python3javacgotypescript-yi-ti-yi-jie-yi-t6vd/
    def maxDistToClosest(self, seats: List[int]) -> int:
        first = last = None
        d = 0
        for i, c in enumerate(seats):
            if c:
                if last is not None:
                    d = max(d, i - last)
                if first is None:
                    first = i
                last = i
        return max(first, len(seats) - last - 1, d // 2)

    # 20230823
    # https://leetcode.cn/problems/count-pairs-of-nodes/description/
    # https://leetcode.cn/problems/count-pairs-of-nodes/solutions/2400682/ji-bai-100cong-shuang-zhi-zhen-dao-zhong-yhze/
    def countPairs(self, n: int, edges: List[List[int]], queries: List[int]) -> List[int]:
        # deg[i] 表示与点 i 相连的边的数目
        deg = [0] * (n + 1)  # 节点编号从 1 到 n
        for x, y in edges:
            deg[x] += 1
            deg[y] += 1
        # 统计每条边的出现次数，注意 1-2 和 2-1 算同一条边
        cnt_e = Counter(tuple(sorted(e)) for e in edges)

        ans = [0] * len(queries)
        sorted_deg = sorted(deg)  # 排序，为了双指针
        for j, q in enumerate(queries):
            left, right = 1, n  # 相向双指针
            while left < right:
                if sorted_deg[left] + sorted_deg[right] <= q:
                    left += 1
                else:
                    ans[j] += right - left
                    right -= 1
            for (x, y), c in cnt_e.items():
                if q < deg[x] + deg[y] <= q + c:
                    ans[j] -= 1
        return ans

    # 20230824
    # https://leetcode.cn/problems/count-servers-that-communicate/description/
    # https://leetcode.cn/problems/count-servers-that-communicate/solutions/2402089/python3javacgotypescript-yi-ti-yi-jie-ji-arec/
    def countServers(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        row = [0] * m
        col = [0] * n
        for i in range(m):
            for j in range(n):
                if grid[i][j]:
                    row[i] += 1
                    col[j] += 1
        return sum(
            grid[i][j] and (row[i] > 1 or col[j] > 1)
            for i in range(m)
            for j in range(n)
        )

    # 20230825
    # https://leetcode.cn/problems/count-good-nodes-in-binary-tree/description/
    # https://leetcode.cn/problems/count-good-nodes-in-binary-tree/solutions/2403677/jian-ji-xie-fa-pythonjavacgojs-by-endles-gwxt/
    def goodNodes(self, root: TreeNode, mx=-inf) -> int:
        if root is None:
            return 0
        left = self.goodNodes(root.left, max(mx, root.val))
        right = self.goodNodes(root.right, max(mx, root.val))
        return left + right + (mx <= root.val)

    # 20230826
    # https://leetcode.cn/problems/summary-ranges/description/
    # https://leetcode.cn/problems/summary-ranges/solutions/2411792/python3javacgotypescript-yi-ti-yi-jie-sh-gpep/
    def summaryRanges(self, nums: List[int]) -> List[str]:
        def f(i: int, j: int) -> str:
            return str(nums[i]) if i == j else f'{nums[i]}->{nums[j]}'

        i = 0
        n = len(nums)
        ans = []
        while i < n:
            j = i
            while j + 1 < n and nums[j + 1] == nums[j] + 1:
                j += 1
            ans.append(f(i, j))
            i = j + 1
        return ans

    # 20230827
    # https://leetcode.cn/problems/merge-intervals/description/
    # https://leetcode.cn/problems/merge-intervals/solutions/2412888/zhi-jie-zhu-xiang-bian-li-by-admiring-me-0fdp/
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort()            
        intervalsnew = [intervals[0]]        
        for v in intervals[1:]:
            if intervalsnew[-1][1] >= v[0]:
                intervalsnew[-1][1] = max(intervalsnew[-1][1] , v[1])
            else:
                intervalsnew.append(v)
        return intervalsnew   

    # 20230828
    # https://leetcode.cn/problems/insert-interval/description/
    # https://leetcode.cn/problems/insert-interval/solutions/2414501/python3javacgotypescript-yi-ti-shuang-ji-2fsw/
    def insert(
        self, intervals: List[List[int]], newInterval: List[int]
    ) -> List[List[int]]:
        def merge(intervals: List[List[int]]) -> List[List[int]]:
            intervals.sort()
            ans = [intervals[0]]
            for s, e in intervals[1:]:
                if ans[-1][1] < s:
                    ans.append([s, e])
                else:
                    ans[-1][1] = max(ans[-1][1], e)
            return ans

        intervals.append(newInterval)
        return merge(intervals)

    # 20230829
    # https://leetcode.cn/problems/binary-trees-with-factors/description/
    # https://leetcode.cn/problems/binary-trees-with-factors/solutions/2416115/cong-ji-yi-hua-sou-suo-dao-di-tui-jiao-n-nbk6/
    def numFactoredBinaryTrees(self, arr: List[int]) -> int:
        arr.sort()
        idx = {x: i for i, x in enumerate(arr)}
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果
        def dfs(i: int) -> int:
            res = 1
            val = arr[i]
            for j in range(i):  # val 的因子一定比 val 小
                x = arr[j]
                if val % x == 0 and val // x in idx:  # 另一个因子 val/x 必须在 arr 中
                    res += dfs(j) * dfs(idx[val // x])
            return res
        return sum(dfs(i) for i in range(len(arr))) % (10 ** 9 + 7)
    
    # 20230830
    # https://leetcode.cn/problems/minimum-jumps-to-reach-home/description/
    # https://leetcode.cn/problems/minimum-jumps-to-reach-home/solutions/2417782/python3javacgotypescript-yi-ti-yi-jie-bf-yfk7/
    def minimumJumps(self, forbidden: List[int], a: int, b: int, x: int) -> int:
        s = set(forbidden)
        q = deque([(0, 1)])
        vis = {(0, 1)}
        ans = 0
        while q:
            for _ in range(len(q)):
                i, k = q.popleft()
                if i == x:
                    return ans
                nxt = [(i + a, 1)]
                if k & 1:
                    nxt.append((i - b, 0))
                for j, k in nxt:
                    if 0 <= j < 6000 and j not in s and (j, k) not in vis:
                        q.append((j, k))
                        vis.add((j, k))
            ans += 1
        return -1

    # 20230831
    # https://leetcode.cn/problems/minimum-degree-of-a-connected-trio-in-a-graph/description/
    # https://leetcode.cn/problems/minimum-degree-of-a-connected-trio-in-a-graph/solutions/2419217/python3gotu-lin-jie-ju-zhen-bian-li-by-f-l6wt/
    def minTrioDegree(self, n: int, edges: List[List[int]]) -> int:
        graph = [[False] * (n + 1) for _ in range(n + 1)]
        degree = [0] * (n + 1)
        
        for u, v in edges:
            graph[u][v] = graph[v][u] = True
            degree[u] += 1
            degree[v] += 1
        
        min_degree = float('inf')
        
        for u in range(1, n + 1):
            for v in range(u + 1, n + 1):
                if graph[u][v]:
                    for w in range(v + 1, n + 1):
                        if graph[v][w] and graph[u][w]:
                            trio_degree = degree[u] + degree[v] + degree[w] - 6
                            min_degree = min(min_degree, trio_degree)
        
        if min_degree == float('inf'):
            return -1
        
        return min_degree

    # 20230901
    # https://leetcode.cn/problems/number-of-ways-to-buy-pens-and-pencils/description/
    # https://leetcode.cn/problems/number-of-ways-to-buy-pens-and-pencils/solutions/2420730/javapython3cmei-ju-shu-xue-bu-da-yu-mu-b-ejjk/
    def waysToBuyPensPencils(self, total: int, cost1: int, cost2: int) -> int:
        # total // cost1为最多能购买的最多钢笔数
        # 从[0, x]枚举买i支钢笔的情况，求出能够买的最多铅笔数y，则买i支钢笔的时候有y+1种购买铅笔的方法
        # 对所有方法求和
        return sum([(total - i * cost1) // cost2 + 1 for i in range(total // cost1 + 1)])

    # 20230902
    # https://leetcode.cn/problems/maximum-enemy-forts-that-can-be-captured/description/?envType=daily-question&envId=2023-09-02
    # https://leetcode.cn/problems/maximum-enemy-forts-that-can-be-captured/solutions/2422340/python3javacgorust-yi-ti-yi-jie-shuang-z-xws6/?envType=daily-question&envId=2023-09-02
    def captureForts(self, forts: List[int]) -> int:
        n = len(forts)
        i = ans = 0
        while i < n:
            j = i + 1
            if forts[i]:
                while j < n and forts[j] == 0:
                    j += 1
                if j < n and forts[i] + forts[j] == 0:
                    ans = max(ans, j - i - 1)
            i = j
        return ans

    # 20230903
    # https://leetcode.cn/problems/eliminate-maximum-number-of-monsters/description/?envType=daily-question&envId=2023-09-03
    # https://leetcode.cn/problems/eliminate-maximum-number-of-monsters/solutions/2423722/python3javacgotypescript-yi-ti-yi-jie-pa-vaoj/?envType=daily-question&envId=2023-09-03
    def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int:
        times = sorted((d - 1) // s for d, s in zip(dist, speed))
        for i, t in enumerate(times):
            if t < i:
                return i
        return len(times)

    # 20230905
    # https://leetcode.cn/problems/form-smallest-number-from-two-digit-arrays/description/?envType=daily-question&envId=2023-09-05
    # https://leetcode.cn/problems/form-smallest-number-from-two-digit-arrays/solutions/2203598/liang-chong-zuo-fa-ha-xi-biao-wei-yun-su-k69f/?envType=daily-question&envId=2023-09-05
    def minNumber(self, nums1: List[int], nums2: List[int]) -> int:
        s = set(nums1) & set(nums2)
        if s: return min(s)  # 有交集
        x, y = min(nums1), min(nums2)
        return min(x * 10 + y, y * 10 + x)

    # 20230906
    # https://leetcode.cn/problems/lowest-common-ancestor-of-deepest-leaves/description/?envType=daily-question&envId=2023-09-06
    # https://leetcode.cn/problems/lowest-common-ancestor-of-deepest-leaves/solutions/2428724/liang-chong-di-gui-si-lu-pythonjavacgojs-xxnk/?envType=daily-question&envId=2023-09-06
    def lcaDeepestLeaves(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        ans = None
        max_depth = -1  # 全局最大深度
        def dfs(node: Optional[TreeNode], depth: int) -> int:
            nonlocal ans, max_depth
            if node is None:
                max_depth = max(max_depth, depth)  # 维护全局最大深度
                return depth
            left_max_depth = dfs(node.left, depth + 1)  # 获取左子树最深叶节点的深度
            right_max_depth = dfs(node.right, depth + 1)  # 获取右子树最深叶节点的深度
            if left_max_depth == right_max_depth == max_depth:
                ans = node
            return max(left_max_depth, right_max_depth)  # 当前子树最深叶节点的深度
        dfs(root, 0)
        return ans

    # 20230907
    # https://leetcode.cn/problems/minimum-time-to-repair-cars/description/?envType=daily-question&envId=2023-09-07
    # https://leetcode.cn/problems/minimum-time-to-repair-cars/solutions/2430475/python3javacgotypescript-yi-ti-yi-jie-er-f96a/?envType=daily-question&envId=2023-09-07
    def repairCars(self, ranks: List[int], cars: int) -> int:
        def check(t: int) -> bool:
            return sum(int(sqrt(t // r)) for r in ranks) >= cars

        return bisect_left(range(ranks[0] * cars * cars), True, key=check)

    # 20230908
    # https://leetcode.cn/problems/calculate-delayed-arrival-time/description/?envType=daily-question&envId=2023-09-08
    # https://leetcode.cn/problems/calculate-delayed-arrival-time/solutions/2241295/an-ti-yi-shi-xian-by-endlesscheng-a34t/?envType=daily-question&envId=2023-09-08
    def findDelayedArrivalTime(self, arrivalTime: int, delayedTime: int) -> int:
        return (arrivalTime + delayedTime) % 24

    # 20230909
    # https://leetcode.cn/problems/course-schedule/description/?envType=daily-question&envId=2023-09-09
    # https://leetcode.cn/problems/course-schedule/solutions/2301411/python3-bfs-by-lebronl-mqeb/?envType=daily-question&envId=2023-09-09
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]):
        adj = [[] for _ in range(numCourses)]
        degrees = [0 for _ in range(numCourses)]

        for p in prerequisites:
            class_1 = p[0]
            class_2 = p[1]
            adj[class_2].append(class_1)
            degrees[class_1] += 1
        deque = collections.deque()

        for i in range(len(degrees)):
            if degrees[i] == 0:
                deque.append(i)
        while deque:
            cla = deque.pop()
            numCourses-=1
            for c in adj[cla]:
                degrees[c] -=1
                if degrees[c] == 0:
                     deque.append(c)
        return not numCourses

    # 20230910
    # https://leetcode.cn/problems/course-schedule-ii/description/?envType=daily-question&envId=2023-09-10
    # https://leetcode.cn/problems/course-schedule-ii/solutions/2435083/python3javacgotypescript-yi-ti-yi-jie-tu-4hqd/?envType=daily-question&envId=2023-09-10
    def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
        g = defaultdict(list)
        indeg = [0] * numCourses
        for a, b in prerequisites:
            g[b].append(a)
            indeg[a] += 1
        ans = []
        q = deque(i for i, x in enumerate(indeg) if x == 0)
        while q:
            i = q.popleft()
            ans.append(i)
            for j in g[i]:
                indeg[j] -= 1
                if indeg[j] == 0:
                    q.append(j)
        return ans if len(ans) == numCourses else []

    # 20230911
    # https://leetcode.cn/problems/course-schedule-iii/description/?envType=daily-question&envId=2023-09-11
    # https://leetcode.cn/problems/course-schedule-iii/solutions/1156601/pythonjavajavascriptgo-tan-xin-by-himymb-04rr/?envType=daily-question&envId=2023-09-11
    def scheduleCourse(self, courses: List[List[int]]) -> int:
        pq, t = [], 0
        for duration, lastDay in sorted(courses, key=lambda x:x[1]):
            if t + duration > lastDay and pq and -pq[0] > duration:
                t += heapq.heappop(pq)
            if t + duration <= lastDay:
                heapq.heappush(pq, -duration)
                t += duration
        return len(pq)

    # 20230912
    # https://leetcode.cn/problems/course-schedule-iv/description/?envType=daily-question&envId=2023-09-12
    # https://leetcode.cn/problems/course-schedule-iv/solutions/2438246/python3javacgotypescript-yi-ti-shuang-ji-0rmo/?envType=daily-question&envId=2023-09-12
    def checkIfPrerequisite(self, n: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]:
        f = [[False] * n for _ in range(n)]
        for a, b in prerequisites:
            f[a][b] = True
        for k in range(n):
            for i in range(n):
                for j in range(n):
                    if f[i][k] and f[k][j]:
                        f[i][j] = True
        return [f[a][b] for a, b in queries]

    # 20230913
    # https://leetcode.cn/problems/check-knight-tour-configuration/?envType=daily-question&envId=2023-09-13
    def checkValidGrid(self, grid: List[List[int]]) -> bool:
        if grid[0][0]: return False # 特判起点
        n = len(grid) # 矩阵长度
        dir = [[-2, 1], [-1, 2], [1, 2], [2, 1], [2, -1], [1, -2], [-1, -2], [-2, -1]] # 8个方向的坐标增量
        x = y = 0 # 当前位置
        for target in range(1, n * n): # 目标值
            for dx, dy in dir: # 遍历8个方向
                xi, yi = x + dx, y + dy # 下一步的位置
                if 0 <= xi < n and 0 <= yi < n and grid[xi][yi] == target: # 下一步的位置没有越界，并且等于目标值
                    x, y = xi, yi # 走到该位置
                    break # 跳出循环
            else: return False # 如果搜索完8个方向都没有匹配到目标值，按照题意，应当返回false
        return True

    # 20230914
    # https://leetcode.cn/problems/queens-that-can-attack-the-king/?envType=daily-question&envId=2023-09-14
    def queensAttacktheKing(self, queens: List[List[int]], king: List[int]) -> List[List[int]]:
        s = set(map(tuple, queens))
        ans = []
        for dx, dy in (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1):
            x, y = king[0] + dx, king[1] + dy
            while 0 <= x < 8 and 0 <= y < 8:
                if (x, y) in s:
                    ans.append([x, y])
                    break
                x += dx
                y += dy
        return ans

    # 20230915
    # https://leetcode.cn/problems/WHnhjV/?envType=daily-question&envId=2023-09-15
    def giveGem(self, gem: List[int], operations: List[List[int]]) -> int:
        for x, y in operations:
            v = gem[x] >> 1
            gem[y] += v
            gem[x] -= v
        return max(gem) - min(gem)

    # 20230916
    # https://leetcode.cn/problems/house-robber/description/?envType=daily-question&envId=2023-09-16
    def rob(self, nums: List[int]) -> int:
        if not nums:
            return 0

        size = len(nums)
        if size == 1:
            return nums[0]
        
        dp = [0] * size
        dp[0] = nums[0]
        dp[1] = max(nums[0], nums[1])
        for i in range(2, size):
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
        
        return dp[size - 1]
    
    # 20230917
    # https://leetcode.cn/problems/house-robber-ii/description/?envType=daily-question&envId=2023-09-17
    def rob(self, nums: [int]) -> int:
        def my_rob(nums):
            cur, pre = 0, 0
            for num in nums:
                cur, pre = max(pre + num, cur), cur
            return cur
        return max(my_rob(nums[:-1]),my_rob(nums[1:])) if len(nums) != 1 else nums[0]

    # 20230918
    # https://leetcode.cn/problems/house-robber-iii/?envType=daily-question&envId=2023-09-18
    def rob(self, root: Optional[TreeNode]) -> int:
        def dfs(root: Optional[TreeNode]) -> (int, int):
            if root is None:
                return 0, 0
            la, lb = dfs(root.left)
            ra, rb = dfs(root.right)
            return root.val + lb + rb, max(la, lb) + max(ra, rb)

        return max(dfs(root))
    
    # 20230919
    # https://leetcode.cn/problems/house-robber-iv/description/?envType=daily-question&envId=2023-09-19
    def minCapability(self, nums: List[int], k: int) -> int:
        # solve(mx) 返回最大金额为 mx 时，最多可以偷多少间房子
        def solve(mx: int) -> int:
            f0 = f1 = 0
            for x in nums:
                if x > mx:
                    f0 = f1
                else:
                    f0, f1 = f1, max(f1, f0 + 1)
            return f1
        return bisect_left(range(max(nums)), k, key=solve)
    
    # 20230920
    # https://leetcode.cn/problems/na-ying-bi/description/?envType=daily-question&envId=2023-09-20
    def minCount(self, coins):
        return sum([(x + 1) // 2 for x in coins])

    # 20230921
    # https://leetcode.cn/problems/collect-coins-in-a-tree/description/?envType=daily-question&envId=2023-09-21
    def collectTheCoins(self, coins: List[int], edges: List[List[int]]) -> int:
        n = len(coins)
        g = [[] for _ in range(n)]
        for x, y in edges:
            g[x].append(y)
            g[y].append(x)  # 建图
        deg = list(map(len, g))  # 每个节点的度数（邻居个数）

        left_edges = n - 1  # 剩余边数
        # 拓扑排序，去掉没有金币的子树
        q = []
        for i, (d, c) in enumerate(zip(deg, coins)):
            if d == 1 and c == 0:  # 没有金币的叶子
                q.append(i)
        while q:
            left_edges -= 1  # 删除节点到其父节点的边
            for y in g[q.pop()]:
                deg[y] -= 1
                if deg[y] == 1 and coins[y] == 0:  # 没有金币的叶子
                    q.append(y)

        # 再次拓扑排序
        for i, (d, c) in enumerate(zip(deg, coins)):
            if d == 1 and c:  # 有金币的叶子（判断 c 是避免把没有金币的叶子也算进来）
                q.append(i)
        left_edges -= len(q)  # 删除所有叶子（到其父节点的边）
        for x in q:  # 遍历所有叶子
            for y in g[x]:
                deg[y] -= 1
                if deg[y] == 1:  # y 现在是叶子了
                    left_edges -= 1  # 删除 y（到其父节点的边）
        return max(left_edges * 2, 0)

    # 20230922
    # https://leetcode.cn/problems/distribute-money-to-maximum-children/description/?envType=daily-question&envId=2023-09-22
    def distMoney(self, money: int, children: int) -> int:
        if money < children:
            return -1
        if money > 8 * children:
            return children - 1
        if money == 8 * children - 4:
            return children - 2
        # money-8x >= children-x, x <= (money-children)/7
        return (money - children) // 7

    # 20230926
    # https://leetcode.cn/problems/pass-the-pillow/description/?envType=daily-question&envId=2023-09-26
    def passThePillow(self, n: int, time: int) -> int:
        time %= (n + n - 2)
        return 1 + (time if time < n else 2 * n - 2 - time)

    # 23230927
    # https://leetcode.cn/problems/filter-restaurants-by-vegan-friendly-price-and-distance/description/?envType=daily-question&envId=2023-09-27
    def filterRestaurants(self,restaurants: List[List[int]], veganFriendly: int, maxPrice: int, maxDistance: int) -> List[int]:
        restaurants.sort(key=lambda x: (-x[1], -x[0]))
        ans = []
        for idx, _, vegan, price, dist in restaurants:
            if vegan >= veganFriendly and price <= maxPrice and dist <= maxDistance:
                ans.append(idx)
        return ans

    # 20230928
    # https://leetcode.cn/problems/number-of-flowers-in-full-bloom/description/?envType=daily-question&envId=2023-09-28
    def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]:
        starts = sorted(s for s, _ in flowers)
        ends = sorted(e for _, e in flowers)
        return [bisect_right(starts, p) - bisect_left(ends, p) for p in people]

    # 20230929
    # https://leetcode.cn/problems/can-place-flowers/description/?envType=daily-question&envId=2023-09-29
    def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
        flowerbed = [0] + flowerbed + [0]
        for i in range(1, len(flowerbed) - 1):
            if flowerbed[i - 1] == 0 and flowerbed[i] == 0 and flowerbed[i + 1] == 0:
                flowerbed[i] = 1  # 种花！
                n -= 1
        return n <= 0

    # 20230930
    # https://leetcode.cn/problems/earliest-possible-day-of-full-bloom/description/?envType=daily-question&envId=2023-09-30
    def earliestFullBloom(self, plantTime: List[int], growTime: List[int]) -> int:
        ans = t = 0
        for pt, gt in sorted(zip(plantTime, growTime), key=lambda x: -x[1]):
            t += pt
            ans = max(ans, t + gt)
        return ans

    # 20231001
    # https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/?envType=daily-question&envId=2023-10-01
    def maxProfit(self, prices: List[int]) -> int:
        cost, profit = float('+inf'), 0
        for price in prices:
            cost = min(cost, price)
            profit = max(profit, price - cost)
        return profit

    # 20231002
    # https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/?envType=daily-question&envId=2023-10-02
    def maxProfit(self, prices: List[int]) -> int:
        profit = 0
        for i in range(1, len(prices)):
            tmp = prices[i] - prices[i - 1]
            if tmp > 0: profit += tmp
        return profit

    # 20231003
    # https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/description/?envType=daily-question&envId=2023-10-03
    def maxProfit(self, prices: List[int]) -> int:
        # 第一次买入，第一次卖出，第二次买入，第二次卖出
        f1, f2, f3, f4 = -prices[0], 0, -prices[0], 0
        for price in prices[1:]:
            f1 = max(f1, -price)
            f2 = max(f2, f1 + price)
            f3 = max(f3, f2 - price)
            f4 = max(f4, f3 + price)
        return f4

    # 20231004
    # https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/description/?envType=daily-question&envId=2023-10-04
    def maxProfit(self, k: int, prices: List[int]) -> int:
        # dp[j][0]表示进行了j次交易后当前不持有股票的最大收益
        # dp[j][1]表示进行了j次交易后当前持有股票的最大收益
        dp = [[0, -sys.maxsize] for _ in range(k + 1)]
        # 初始化，k = 0时；不交易持有股票的最大收益为0；不交易持有股票的最大收益为买入首支股票
        # 而k>0时，不持有股票肯定是没有进行交易，收益为0；由于初始不可能进行交易，因此使用一个最小值表示持有股票的非法状态
        dp[0] = [0, - prices[0]]
        # 枚举[0, i]区间[1, j]的最大收益
        for i in range(1, len(prices)):
            dp[0][1] = max(dp[0][1], -prices[i])  # [0, i]进行0次交易后的最大收益，要么保持原有状态，要么就是买入当前股票
            for j in range(1, k + 1):
                dp[j][0] = max(dp[j-1][1] + prices[i], dp[j][0])  # j次交易后不持有股票的最大收益，要么保持，要么从j-1次交易中持有的股票卖出
                dp[j][1] = max(dp[j][0] - prices[i], dp[j][1])    # j次交易后持有股票的最大收益，要么保持，要么从j次交易不持有股票买入
        return max(0, dp[k][0], dp[k][1])   # 取持有和不持有的最大收益中的最大值，且最大值不小于0

    # 20231005
    # https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/description/?envType=daily-question&envId=2023-10-05
    def maxProfit(self, prices: List[int]) -> int:
        @cache
        def dfs(i: int, j: int) -> int:
            if i >= len(prices):
                return 0
            ans = dfs(i + 1, j)
            if j:
                ans = max(ans, prices[i] + dfs(i + 2, 0))
            else:
                ans = max(ans, -prices[i] + dfs(i + 1, 1))
            return ans

        return dfs(0, 0)

    # 20231006
    # https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/?envType=daily-question&envId=2023-10-06
    def maxProfit(self, prices: List[int], fee: int) -> int:
        n = len(prices)
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果
        def dfs(i: int, hold: bool) -> int:
            if i < 0:
                return -inf if hold else 0
            if hold:
                return max(dfs(i - 1, True), dfs(i - 1, False) - prices[i])
            return max(dfs(i - 1, False), dfs(i - 1, True) + prices[i] - fee)
        return dfs(n - 1, False)3

    # 20231009
    # https://leetcode.cn/problems/split-with-minimum-sum/description/?envType=daily-question&envId=2023-10-09
    def splitNum(self, num: int) -> int:
        s = sorted(str(num))
        return int(''.join(s[::2])) + int(''.join(s[1::2]))

    # 20231010
    # https://leetcode.cn/problems/movement-of-robots/description/?envType=daily-question&envId=2023-10-10
    def sumDistance(self, nums: List[int], s: str, d: int) -> int:
        mod = 10**9 + 7
        for i, c in enumerate(s):
            nums[i] += d if c == "R" else -d
        nums.sort()
        ans = s = 0
        for i, x in enumerate(nums):
            ans += i * x - s
            s += x
        return ans % mod

    # 20231011
    # https://leetcode.cn/problems/reward-top-k-students/description/?envType=daily-question&envId=2023-10-11
    def topStudents(
        self,
        positive_feedback: List[str],
        negative_feedback: List[str],
        report: List[str],
        student_id: List[int],
        k: int,
    ) -> List[int]:
        ps = set(positive_feedback)
        ns = set(negative_feedback)
        arr = []
        for sid, r in zip(student_id, report):
            t = 0
            for w in r.split():
                if w in ps:
                    t += 3
                elif w in ns:
                    t -= 1
            arr.append((t, sid))
        arr.sort(key=lambda x: (-x[0], x[1]))
        return [v[1] for v in arr[:k]]

    # 20231012
    # https://leetcode.cn/problems/find-the-array-concatenation-value/description/?envType=daily-question&envId=2023-10-12
    def findTheArrayConcVal(self, nums: List[int]) -> int:
        ans = 0
        i, j = 0, len(nums) - 1
        while i < j:
            ans += int(str(nums[i]) + str(nums[j]))
            i, j = i + 1, j - 1
        if i == j:
            ans += nums[i]
        return ans

    # 20231013
    # https://leetcode.cn/problems/avoid-flood-in-the-city/description/?envType=daily-question&envId=2023-10-13
    def avoidFlood(self, rains: List[int]) -> List[int]:
        n = len(rains)
        ans = [-1] * n
        fa = [i for i in range(n + 1)]

        def find(x : int) -> int:
            if x == fa[x]: return x
            fa[x] = find(fa[x])
            return fa[x]

        # 记录某个数上一次出现的下标
        last = {}
        
        for i,x in enumerate(rains):
            if x == 0:
                continue

            # 第一次出现
            if x not in last:
                last[x] = i
                continue
            
            # idx 表示需要找的那个 0 的下标
            idx = find(last[x])  # 这里还不一定是需要的 0 的下标

            # 经过这个 while 循环，idx 去寻找满足要求 的 0 (未被使用)
            while idx < i and (rains[idx] != 0 or ans[idx] != -1):
                # 以下两句是关键
                fa[idx] = idx + 1
                idx = find(idx + 1)
                    
            if idx < i and rains[idx] == 0 and ans[idx] == -1:
                ans[idx] = x
            else:
                return []

            last[x] = i
        
        # 为什么会有下面这个循环，看讨论区吐槽
        for i in range(n):
            if rains[i] == 0 and ans[i] == -1:
                ans[i] = 1

        return ans

    # 20231014
    # https://leetcode.cn/problems/single-number/description/?envType=daily-question&envId=2023-10-14
    def singleNumber(self, nums: List[int]) -> List[int]:
        x = 0
        for num in nums:  # 1. 遍历 nums 执行异或运算
            x ^= num      
        return x;         # 2. 返回出现一次的数字 x

    # 20231015
    # https://leetcode.cn/problems/single-number-ii/description/?envType=daily-question&envId=2023-10-15
    def singleNumber(self, nums: List[int]) -> int:
        counts = [0] * 32
        for num in nums:
            for j in range(32):
                counts[j] += num & 1
                num >>= 1
        res, m = 0, 3
        for i in range(32):
            res <<= 1
            res |= counts[31 - i] % m
        return res if counts[31] % m == 0 else ~(res ^ 0xffffffff)

    # 20231016
    # https://leetcode.cn/problems/single-number-iii/description/?envType=daily-question&envId=2023-10-16
    def singleNumber(self, nums: List[int]) -> List[int]:
        xor_all = reduce(xor, nums)
        lowbit = xor_all & -xor_all
        ans = [0, 0]
        for x in nums:
            ans[(x & lowbit) != 0] ^= x  # 分组异或
        return ans

    # 20231017
    # https://leetcode.cn/problems/sum-multiples/description/?envType=daily-question&envId=2023-10-17
    def sumOfMultiples(self, n: int) -> int:
        def s(m: int) -> int:
            return n // m * (n // m + 1) // 2 * m
        return s(3) + s(5) + s(7) - s(15) - s(21) - s(35) + s(105)

    # 20231018
    # https://leetcode.cn/problems/maximal-score-after-applying-k-operations/description/?envType=daily-question&envId=2023-10-18
    def maxKelements(self, nums: List[int], k: int) -> int:
        bigHeap = [-num for num in nums]  # 定义一个大顶堆；默认小顶堆，为了实现大顶堆，元素取反再入堆
        heapq.heapify(bigHeap)     # 堆内元素堆化
        score = 0
        for _ in range(k):
            num = -heapq.heappop(bigHeap)   # 返回当前堆中最大值，累加到得分
            score += num
            heapq.heappush(bigHeap, -((num + 2) // 3))   # 将最大值替换，重新入堆筛选
        return score
    
    # 20231019
    # https://leetcode.cn/problems/tuple-with-same-product/description/?envType=daily-question&envId=2023-10-19
    def tupleSameProduct(self, nums: List[int]) -> int:
        mul_count = {}   # 存储数组中两两的乘积的个数
        n = len(nums)
        for i in range(n):
            for j in range(i+1, n):
                mul = nums[i] * nums[j]
                mul_count[mul] = mul_count.get(mul, 0) + 1   # 统计不同两个元素的乘积个数
        res = 0
        for cnt in mul_count.values():     # 枚举哈希表所有乘积的个数情况
            res += (cnt) * (cnt - 1) * 4   # 每一种乘积取出两组元素，共有n(n-1)/2，两组元素共有8种情况
        return res

    # 20231020
    # https://leetcode.cn/problems/categorize-box-according-to-criteria/description/?envType=daily-question&envId=2023-10-20
    def categorizeBox(self, length: int, width: int, height: int, mass: int) -> str:
        v = length * width * height
        bulky = int(any(x >= 10000 for x in (length, width, height)) or v >= 10**9)
        heavy = int(mass >= 100)
        i = heavy << 1 | bulky
        d = ['Neither', 'Bulky', 'Heavy', 'Both']
        return d[i]

    # 20231021
    # https://leetcode.cn/problems/count-unreachable-pairs-of-nodes-in-an-undirected-graph/description/?envType=daily-question&envId=2023-10-21
    def countPairs(self, n: int, edges: List[List[int]]) -> int:
        self.visited = [False] * n   # 记录遍历过的节点
        # 建图
        links = [[] for _ in range(n)]
        for x, y in edges:
            links[x].append(y)
            links[y].append(x)
        # 深度优先搜索
        res = 0
        for i in range(n):
            if self.visited[i]: continue     # 已搜索过的连通块的节点，跳过
            cnt = self.dfs(i, links)   # 搜索节点所在的连通块的节点数
            res += cnt * (n - cnt)     # 一个连通块的所有节点和连通块以外的节点都互相不可达
        return res // 2     # 每个节点对被算了两次，除以2

        def dfs(self, node:int, links: List[List[int]]) -> int:
            self.visited[node] = True   # 标记当前节点已经访问过了
            cnt = 1     # 统计从这个节点可达的不重复的节点个数，初始个数为一个，表示节点本身
            # 深度优先搜索，找到这个节点所在的连通块的所有节点 
            for next_ in links[node]:
                if not self.visited[next_]: cnt += self.dfs(next_, links)    # 递归搜索相邻节点的可达不重复节点个数
            return cnt
    
    # 20231022
    # https://leetcode.cn/problems/reducing-dishes/description/?envType=daily-question&envId=2023-10-22
    def maxSatisfaction(self, satisfaction: List[int]) -> int:
        satisfaction.sort()
        n = len(satisfaction)
        
        post = 0
        start = n
        for i in range(n - 1, -1, -1):
            post += satisfaction[i]
            if post <= 0:
                break
            start = i

        if start == n:
            return 0
        return sum(satisfaction[j] * (j - start + 1) for j in range(start, n))

    # 20231023
    # https://leetcode.cn/problems/number-of-senior-citizens/description/?envType=daily-question&envId=2023-10-23
    def countSeniors(self, details: List[str]) -> int:
        return sum(int(x[11:13]) > 60 for x in details)
        
    # 20231024
    # https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/description/?envType=daily-question&envId=2023-10-24
    def numRollsToTarget(self, n: int, k: int, target: int) -> int:
        if not (n <= target <= n * k):
            return 0  # 无法组成 target
        MOD = 10 ** 9 + 7
        f = [[0] * (target - n + 1) for _ in range(n + 1)]
        f[0][0] = 1  # dfs(0, 0) = 1
        for i in range(1, n + 1):
            for j in range(target - n + 1):
                for x in range(min(k, j + 1)):  # 掷出了 x
                    f[i][j] = (f[i][j] + f[i - 1][j - x]) % MOD
        return f[n][-1]

    # 20231025
    # https://leetcode.cn/problems/find-the-punishment-number-of-an-integer/description/?envType=daily-question&envId=2023-10-25
    def punishmentNumber(self, n: int) -> int:
        A = [1, 9, 10, 36, 45, 55, 82, 91, 99, 100, 235, 297, 369, 370, 379, 414, 657, 675, 703, 756, 792, 909, 918, 945, 964, 990, 991, 999, 1000, 1296]
        return sum(x * x for x in A if x <= n)
    
    # 20231026
    # https://leetcode.cn/problems/count-the-digits-that-divide-a-number/description/?envType=daily-question&envId=2023-10-26
    def countDigits(self, num: int) -> int:
        return sum(num % int(i) == 0 for i in str(num))

    # 20231027
    # https://leetcode.cn/problems/maximum-area-of-a-piece-of-cake-after-horizontal-and-vertical-cuts/description/?envType=daily-question&envId=2023-10-27
    def maxArea(self, h: int, w: int, hs: List[int], vs: List[int]) -> int:
        MOD = 10 ** 9 + 7
        hs.sort()
        vs.sort()
        n, m = len(hs), len(vs)
        mh, mv = max(hs[0], h - hs[n - 1]), max(vs[0], w - vs[m - 1])
        for i in range(1, n):
            mh = max(mh, hs[i] - hs[i - 1])
        for i in range(1, m):
            mv = max(mv, vs[i] - vs[i - 1])
        return (mh * mv) % MOD
    
    # 20231028
    # https://leetcode.cn/problems/take-gifts-from-the-richest-pile/description/?envType=daily-question&envId=2023-10-28
    def pickGifts(self, gifts: List[int], k: int) -> int:
        for i in range(len(gifts)):
            gifts[i] *= -1  # 变成相反数，这样堆化后就是最大堆了
        heapify(gifts)  # 原地堆化
        while k and -gifts[0] > 1:
            heapreplace(gifts, -isqrt(-gifts[0]))  # 直接修改堆顶
            k -= 1
        return -sum(gifts)

    # 20231029
    # https://leetcode.cn/problems/h-index/description/?envType=daily-question&envId=2023-10-29
    def hIndex(self, cs: List[int]) -> int:
        n = len(cs)
        l, r = 0, n
        while l < r:
            mid = (l + r + 1) // 2
            if sum(c >= mid for c in cs) >= mid:
                l = mid
            else:
                r = mid - 1
        return r

    # 20231030
    # https://leetcode.cn/problems/h-index-ii/description/?envType=daily-question&envId=2023-10-30
    def hIndex(self, citations: List[int]) -> int:
        # 在区间 (left, right] 内询问
        left = 0
        right = len(citations)
        while left < right:  # 区间不为空
            # 循环不变量：
            # left 的回答一定为「是」
            # right+1 的回答一定为「否」
            mid = (left + right + 1) // 2  # 保证 mid 在二分区间内
            # 引用次数最多的 mid 篇论文，引用次数均 >= mid
            if citations[-mid] >= mid:
                left = mid  # 询问范围缩小到 (mid, right]
            else:
                right = mid - 1  # 询问范围缩小到 (left, mid-1]
        # 根据循环不变量，left 现在是最大的回答为「是」的数
        return left

    # 20231031
    # https://leetcode.cn/problems/smallest-missing-genetic-value-in-each-subtree/description/?envType=daily-question&envId=2023-10-31
    def smallestMissingValueSubtree(self, parents: List[int], nums: List[int]) -> List[int]:
        n = len(parents)
        ans = [1] * n
        if 1 not in nums:  # 不存在基因值为 1 的点
            return ans

        # 建树
        g = [[] for _ in range(n)]
        for i in range(1, n):
            g[parents[i]].append(i)

        vis = [False] * (n + 2)
        mex = 2  # 缺失的最小基因值
        pre = -1
        node = nums.index(1)  # 出发点
        while node >= 0:
            vis[min(nums[node], n + 1)] = True  # 标记基因值
            nodes = [son for son in g[node] if son != pre]
            while nodes:
                x = nodes.pop()
                vis[min(nums[x], n + 1)] = True  # 标记基因值
                nodes.extend(g[x])  # 保存接下来需要遍历的点
            while vis[mex]:  # node 子树包含这个基因值
                mex += 1
            ans[node] = mex  # 缺失的最小基因值
            pre = node  # 下一轮循环不会遍历 pre 子树
            node = parents[node]  # 往上走
        return ans

    # 20231101
    # https://leetcode.cn/problems/maximum-employees-to-be-invited-to-a-meeting/description/?envType=daily-question&envId=2023-11-01
    def maximumInvitations(self, favorite: List[int]) -> int:
        n = len(favorite)
        deg = [0] * n
        for f in favorite:
            deg[f] += 1  # 统计基环树每个节点的入度

        rg = [[] for _ in range(n)]  # 反图
        q = deque(i for i, d in enumerate(deg) if d == 0)
        while q:  # 拓扑排序，剪掉图上所有树枝
            x = q.popleft()
            y = favorite[x]  # x 只有一条出边
            rg[y].append(x)
            deg[y] -= 1
            if deg[y] == 0:
                q.append(y)

        # 通过反图 rg 寻找树枝上最深的链
        def rdfs(x: int) -> int:
            max_depth = 1
            for son in rg[x]:
                max_depth = max(max_depth, rdfs(son) + 1)
            return max_depth

        max_ring_size = sum_chain_size = 0
        for i, d in enumerate(deg):
            if d == 0: continue

            # 遍历基环上的点
            deg[i] = 0  # 将基环上的点的入度标记为 0，避免重复访问
            ring_size = 1  # 基环长度
            x = favorite[i]
            while x != i:
                deg[x] = 0  # 将基环上的点的入度标记为 0，避免重复访问
                ring_size += 1
                x = favorite[x]

            if ring_size == 2:  # 基环长度为 2
                sum_chain_size += rdfs(i) + rdfs(favorite[i])  # 累加两条最长链的长度
            else:
                max_ring_size = max(max_ring_size, ring_size)  # 取所有基环长度的最大值
        return max(max_ring_size, sum_chain_size)

    # 20231102
    # https://leetcode.cn/problems/rings-and-rods/description/?envType=daily-question&envId=2023-11-02
    def countPoints(self, rings: str) -> int:
        mask = [0] * 10
        d = {"R": 1, "G": 2, "B": 4}
        for i in range(0, len(rings), 2):
            c = rings[i]
            j = int(rings[i + 1])
            mask[j] |= d[c]
        return mask.count(7)
    
    # 20231103
    # https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii/description/?envType=daily-question&envId=2023-11-03
    def connect(self, root: 'Node') -> 'Node':
        pre = []
        def dfs(node: 'Node', depth: int) -> None:
            if node is None:
                return
            if depth == len(pre):  # node 是这一层最左边的节点
                pre.append(node)
            else:  # pre[depth] 是 node 左边的节点
                pre[depth].next = node  # node 左边的节点指向 node
                pre[depth] = node
            dfs(node.left, depth + 1)
            dfs(node.right, depth + 1)
        dfs(root, 0)  # 根节点的深度为 0
        return root

    # 20231104
    # https://leetcode.cn/problems/maximum-xor-of-two-numbers-in-an-array/description/?envType=daily-question&envId=2023-11-04
   def findMaximumXOR(self, nums: List[int]) -> int:
        ans = mask = 0
        high_bit = max(nums).bit_length() - 1
        for i in range(high_bit, -1, -1):  # 从最高位开始枚举
            mask |= 1 << i
            new_ans = ans | (1 << i)  # 这个比特位可以是 1 吗？
            seen = set()
            for x in nums:
                x &= mask  # 低于 i 的比特位置为 0
                if new_ans ^ x in seen:
                    ans = new_ans  # 这个比特位可以是 1
                    break
                seen.add(x)
        return ans

    # 20231105
    # https://leetcode.cn/problems/repeated-dna-sequences/description/?envType=daily-question&envId=2023-11-05
    def findRepeatedDnaSequences(self, s: str):
        if len(s) < 10:
            return []
        
        sequences = set()
        repeated_sequences = set()
        
        for i in range(len(s) - 9):
            sequence = s[i:i+10]
            if sequence in sequences:
                repeated_sequences.add(sequence)
            else:
                sequences.add(sequence)
        
        return list(repeated_sequences)

    # 20231106
    # https://leetcode.cn/problems/maximum-product-of-word-lengths/description/?envType=daily-question&envId=2023-11-06
    def maxProduct(self, words: List[str]) -> int:
        mask = [0] * len(words)
        ans = 0
        for i, s in enumerate(words):
            for c in s:
                mask[i] |= 1 << (ord(c) - ord("a"))
            for j, t in enumerate(words[:i]):
                if (mask[i] & mask[j]) == 0:
                    ans = max(ans, len(s) * len(t))
        return ans

    # 20231107
    # https://leetcode.cn/problems/count-the-number-of-vowel-strings-in-range/description/?envType=daily-question&envId=2023-11-07
    def vowelStrings(self, words: List[str], left: int, right: int) -> int:
        return sum(1 for i in range(left, right + 1) if words[i][0] in "aeiou" and words[i][-1] in "aeiou")

    # 20231108
    # https://leetcode.cn/problems/find-the-longest-balanced-substring-of-a-binary-string/description/?envType=daily-question&envId=2023-11-08
    def findTheLongestBalancedSubstring(self, s: str) -> int:
        n, idx, ans = len(s), 0, 0
        while idx < n:
            a, b = 0, 0
            while idx < n and s[idx] == '0':
                a, idx = a + 1, idx + 1
            while idx < n and s[idx] == '1':
                b, idx = b + 1, idx + 1
            ans = max(ans, min(a, b) * 2)
        return ans

    # 20231109
    # https://leetcode.cn/problems/escape-the-spreading-fire/description/?envType=daily-question&envId=2023-11-09
    def maximumMinutes(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])

        # 返回能否在初始位置停留 t 分钟，并安全到达安全屋
        def check(t: int) -> bool:
            f = [(i, j) for i, row in enumerate(grid) for j, x in enumerate(row) if x == 1]
            on_fire = set(f)  # 标记着火的位置
            def spread_fire():
                # 火的 BFS
                nonlocal f
                tmp = f
                f = []
                for i, j in tmp:
                    for x, y in (i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1):  # 上下左右
                        if 0 <= x < m and 0 <= y < n and grid[x][y] == 0 and (x, y) not in on_fire:
                            on_fire.add((x, y))  # 标记着火的位置
                            f.append((x, y))
            while t and f:  # 如果火无法扩散就提前退出
                spread_fire()  # 火扩散
                t -= 1
            if (0, 0) in on_fire:
                return False  # 起点着火，寄

            # 人的 BFS
            q = [(0, 0)]
            vis = set(q)
            while q:
                tmp = q
                q = []
                for i, j in tmp:
                    if (i, j) in on_fire: continue  # 人走到这个位置后，火也扩散到了这个位置
                    for x, y in (i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1):  # 上下左右
                        if 0 <= x < m and 0 <= y < n and grid[x][y] == 0 and (x, y) not in on_fire and (x, y) not in vis:
                            if x == m - 1 and y == n - 1:
                                return True  # 我们安全了…暂时。
                            vis.add((x, y))  # 避免反复访问同一个位置
                            q.append((x, y))
                spread_fire()  # 火扩散
            return False  # 人被火烧到，或者没有可以到达安全屋的路

        # 这里我用开区间二分（其它写法也可以）
        left = -1
        right = m * n + 1
        while left + 1 < right:
            mid = (left + right) // 2
            if check(mid):
                left = mid
            else:
                right = mid
        return left if left < m * n else 10 ** 9

    # 20231110
    # https://leetcode.cn/problems/successful-pairs-of-spells-and-potions/description/?envType=daily-question&envId=2023-11-10
    def successfulPairs(self, a: List[int], b: List[int], t: int) -> List[int]:
        b.sort()
        return [len(b) - bisect_left(b, t / x) for x in a]

    # 20231111
    # https://leetcode.cn/problems/couples-holding-hands/description/?envType=daily-question&envId=2023-11-11
    def minSwapsCouples(self, row: List[int]) -> int:
        def find(x: int) -> int:
            if p[x] != x:
                p[x] = find(p[x])
            return p[x]

        n = len(row) >> 1
        p = list(range(n))
        for i in range(0, len(row), 2):
            a, b = row[i] >> 1, row[i + 1] >> 1
            p[find(a)] = find(b)
        return n - sum(i == find(i) for i in range(n))

    # 20231114
    # https://leetcode.cn/problems/find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance/?envType=daily-question&envId=2023-11-14
    def findTheCity(self, n: int, edges: List[List[int]], distanceThreshold: int) -> int:
        w = [[inf] * n for _ in range(n)]
        for x, y, wt in edges:
            w[x][y] = w[y][x] = wt

        @cache  # 缓存装饰器，避免重复计算 dfs 的结果
        def dfs(k: int, i: int, j: int) -> int:
            if k < 0:  # 递归边界
                return w[i][j]
            return min(dfs(k - 1, i, j), dfs(k - 1, i, k) + dfs(k - 1, k, j))

        ans = 0
        min_cnt = inf
        for i in range(n):
            cnt = 0
            for j in range(n):
                if j != i and dfs(n - 1, i, j) <= distanceThreshold:
                    cnt += 1
            if cnt <= min_cnt:  # 相等时取最大的 i
                min_cnt = cnt
                ans = i
        return ans

    # 20231115
    # https://leetcode.cn/problems/maximum-sum-with-exactly-k-elements/description/?envType=daily-question&envId=2023-11-15
    def maximizeSum(self, nums: List[int], k: int) -> int:
        return k * (2 * max(nums) + k - 1) // 2

    # 20231116
    # https://leetcode.cn/problems/longest-even-odd-subarray-with-threshold/description/?envType=daily-question&envId=2023-11-16
    def longestAlternatingSubarray(self, nums: List[int], threshold: int) -> int:
        n = len(nums)
        ans = i = 0
        while i < n:
            if nums[i] > threshold or nums[i] % 2:
                i += 1  # 直接跳过
                continue
            start = i  # 记录这一组的开始位置
            i += 1  # 开始位置已经满足要求，从下一个位置开始判断
            while i < n and nums[i] <= threshold and nums[i] % 2 != nums[i - 1] % 2:
                i += 1
            # 从 start 到 i-1 是满足题目要求的子数组
            ans = max(ans, i - start)
        return ans

    # 20231117
    # https://leetcode.cn/problems/maximum-sum-queries/description/?envType=daily-question&envId=2023-11-17
    def maximumSumQueries(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:
        ans = [-1] * len(queries)
        a = sorted(((a, b) for a, b in zip(nums1, nums2)), key=lambda p: -p[0])
        j = 0
        st = []
        for i, (x, y) in sorted(enumerate(queries), key=lambda p: -p[1][0]):
            while j < len(a) and a[j][0] >= x:  # 下面只需关心 ay (a[j][1])
                ax, ay = a[j]
                while st and st[-1][1] <= ax + ay:  # ay >= st[-1][0]
                    st.pop()
                if not st or st[-1][0] < ay:
                    st.append((ay, ax + ay))
                j += 1
            p = bisect_left(st, (y,))
            if p < len(st):
                ans[i] = st[p][1]
        return ans

    # 20231118
    # https://leetcode.cn/problems/max-sum-of-a-pair-with-equal-sum-of-digits/description/?envType=daily-question&envId=2023-11-18
    def maximumSum(self, nums: List[int]) -> int:
        val = [[0, 0] for _ in range(100)]
        for x in nums:
            t, cur = x, 0
            while t != 0:
                cur += t % 10
                t //= 10
            if x >= val[cur][1]:
                val[cur][0], val[cur][1] = val[cur][1], x
            elif x > val[cur][0]:
                val[cur][0] = x
        ans = -1
        for i in range(100):
            if val[i][0] != 0 and val[i][1] != 0:
                ans = max(ans, val[i][0] + val[i][1])
        return ans

    # 20231119
    # https://leetcode.cn/problems/maximum-sum-of-3-non-overlapping-subarrays/description/?envType=daily-question&envId=2023-11-19
    def maxSumOfThreeSubarrays(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        s = list(accumulate(nums, initial=0))
        pre = [[] for _ in range(n)]
        suf = [[] for _ in range(n)]
        t = idx = 0
        for i in range(n - k + 1):
            if (cur := s[i + k] - s[i]) > t:
                pre[i + k - 1] = [cur, i]
                t, idx = pre[i + k - 1]
            else:
                pre[i + k - 1] = [t, idx]
        t = idx = 0
        for i in range(n - k, -1, -1):
            if (cur := s[i + k] - s[i]) >= t:
                suf[i] = [cur, i]
                t, idx = suf[i]
            else:
                suf[i] = [t, idx]
        t = 0
        ans = []
        for i in range(k, n - 2 * k + 1):
            if (cur := s[i + k] - s[i] + pre[i - 1][0] + suf[i + k][0]) > t:
                ans = [pre[i - 1][1], i, suf[i + k][1]]
                t = cur
        return ans

    # 20231120
    # https://leetcode.cn/problems/maximum-subarray/description/?envType=daily-question&envId=2023-11-20
    def maxSubArray(self, nums: List[int]) -> int:
        size = len(nums)
        if size == 0:
            return 0
        dp = [0 for _ in range(size)]

        dp[0] = nums[0]
        for i in range(1, size):
            if dp[i - 1] >= 0:
                dp[i] = dp[i - 1] + nums[i]
            else:
                dp[i] = nums[i]
        return max(dp)

    # 20231121
    # https://leetcode.cn/problems/minimum-deletions-to-make-array-beautiful/description/?envType=daily-question&envId=2023-11-21
    def minDeletion(self, nums: List[int]) -> int:
        n = len(nums)    # 数组长度
        i = 0      # 指向当前数组偶数索引
        delCnt = 0    # 删除个数
        while i < n:
            j = i + 1  
            while j < n and nums[i] == nums[j]: j += 1    # 找偶数索引右侧索引首个不为nums[i]的位置
            delCnt += j - (i + 1)  # [i+1, j)的元素都与nums[i]相等，要删除
            i = j + 1  # j为删除i的下一个元素，因此下一个偶数索引应该为i的下下个元素，即为j的下个元素

        return delCnt + ((n - delCnt) & 1)    # 如果删除后数组元素个数为奇数，则要把最后一个元素删除，即多删一个元素
    
    # 20231122
    # https://leetcode.cn/problems/minimum-deletions-to-make-array-beautiful/description/?envType=daily-question&envId=2023-11-22
    def minDeletion(self, nums: List[int]) -> int:
        n = len(nums)    # 数组长度
        i = 0      # 指向当前数组偶数索引
        delCnt = 0    # 删除个数
        while i < n:
            j = i + 1  
            while j < n and nums[i] == nums[j]: j += 1    # 找偶数索引右侧索引首个不为nums[i]的位置
            delCnt += j - (i + 1)  # [i+1, j)的元素都与nums[i]相等，要删除
            i = j + 1  # j为删除i的下一个元素，因此下一个偶数索引应该为i的下下个元素，即为j的下个元素

        return delCnt + ((n - delCnt) & 1)    # 如果删除后数组元素个数为奇数，则要把最后一个元素删除，即多删一个元素

    # 20231123
    # https://leetcode.cn/problems/html-entity-parser/description/?envType=daily-question&envId=2023-11-23
    def entityParser(self, text: str) -> str:
        entity_map = {
            "&quot;": "\"",
            "&apos;": "'",
            "&amp;": "&",
            "&gt;": ">",
            "&lt;": "<",
            "&frasl;": "/"
        }
        i, n = 0, len(text)
        ans = ""
        while i < n:
            if text[i] == '&':
                j = i + 1
                while j < n and j - i < 6 and text[j] != ';':
                    j += 1
                sub = text[i:min(j + 1, n)]
                if sub in entity_map:
                    ans += entity_map[sub]
                    i = j + 1
                    continue
            ans += text[i]
            i += 1
        return ans

    # 20231124
    # https://leetcode.cn/problems/count-pairs-whose-sum-is-less-than-target/description/?envType=daily-question&envId=2023-11-24
    def countPairs(self, nums: List[int], target: int) -> int:
        n = len(nums)
        count = 0

        for i in range(n):
            for j in range(i + 1, n):
                if nums[i] + nums[j] < target:
                    count += 1

        return count

    # 20231125
    # https://leetcode.cn/problems/pseudo-palindromic-paths-in-a-binary-tree/description/?envType=daily-question&envId=2023-11-25
    def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int:
        def dfs(root: Optional[TreeNode], mask: int):
            if root is None:
                return 0
            mask ^= 1 << root.val
            if root.left is None and root.right is None:
                return int((mask & (mask - 1)) == 0)
            return dfs(root.left, mask) + dfs(root.right, mask)

        return dfs(root, 0)

    # 20231126
    # https://leetcode.cn/problems/count-unique-characters-of-all-substrings-of-a-given-string/description/?envType=daily-question&envId=2023-11-26
    def uniqueLetterString(self, s: str) -> int:
        d = defaultdict(list)
        for i, c in enumerate(s):
            d[c].append(i)
        ans = 0
        for v in d.values():
            v = [-1] + v + [len(s)]
            for i in range(1, len(v) - 1):
                ans += (v[i] - v[i - 1]) * (v[i + 1] - v[i])
        return ans

    # 20231127
    # https://leetcode.cn/problems/sum-of-subarray-minimums/description/?envType=daily-question&envId=2023-11-27
    def sumSubarrayMins(self, arr: List[int]) -> int:
        n = len(arr)
        left = [-1] * n
        right = [n] * n
        stk = []
        for i, v in enumerate(arr):
            while stk and arr[stk[-1]] >= v:
                stk.pop()
            if stk:
                left[i] = stk[-1]
            stk.append(i)

        stk = []
        for i in range(n - 1, -1, -1):
            while stk and arr[stk[-1]] > arr[i]:
                stk.pop()
            if stk:
                right[i] = stk[-1]
            stk.append(i)
        mod = 10**9 + 7
        return sum((i - left[i]) * (right[i] - i) * v for i, v in enumerate(arr)) % mod

    # 20231130
    # https://leetcode.cn/problems/determine-if-two-strings-are-close/description/?envType=daily-question&envId=2023-11-30
    def closeStrings(self, word1: str, word2: str) -> bool:
        cnt1, cnt2 = Counter(word1), Counter(word2)
        return sorted(cnt1.values()) == sorted(cnt2.values()) and set(
            cnt1.keys()
        ) == set(cnt2.keys())

    # 20231201
    # https://leetcode.cn/problems/first-completely-painted-row-or-column/description/?envType=daily-question&envId=2023-12-01
    def firstCompleteIndex(self, arr: List[int], mat: List[List[int]]) -> int:
        m = len(mat)    # 获取行数
        n = len(mat[0])    # 获取列数
        value2MatId = {mat[r][c]: r*n+c for c in range(n) for r in range(m)}    # 存储每个数值和在mat中的位置编号id
        row_color_cnts = [0] * m   # row_color_cnts[i]表示第i行当前涂色格子个数
        col_color_cnts = [0] * n    # col_color_cnts[i]表示第i行当前涂色格子个数
        for i, val in enumerate(arr):
            r, c = divmod(value2MatId[val], n)     # 根据位置id还原位置行列(r, c)
            row_color_cnts[r] += 1      # 所在行涂色格子数 + 1 
            col_color_cnts[c] += 1      # 所在列涂色格子数 + 1
            if row_color_cnts[r] == n: return i   # 所在行涂满了，i就是首先涂满某一行的最小下标
            if col_color_cnts[c] == m: return i   # 所在行涂满了，i就是首先涂满某一列的最小下标
        return -1

    # 20231202
    # https://leetcode.cn/problems/car-pooling/description/?envType=daily-question&envId=2023-12-02
    def carPooling(self, trips: List[List[int]], capacity: int) -> bool:
        d = Counter()
        for num, from_, to in trips:
            d[from_] += num
            d[to] -= num
        s = 0
        for k in sorted(d):
            s += d[k]
            if s > capacity:
                return False
        return True

    # 20231203
    # https://leetcode.cn/problems/maximum-points-you-can-obtain-from-cards/description/?envType=daily-question&envId=2023-12-03
    def maxScore(self, cardPoints: List[int], k: int) -> int:
        n = len(cardPoints)
        m = n - k
        min_s = s = sum(cardPoints[:m])
        for i in range(m, n):
            s += cardPoints[i] - cardPoints[i - m]
            min_s = min(min_s, s)
        return sum(cardPoints) - min_s

    # 20231204
    # https://leetcode.cn/problems/binary-search-tree-to-greater-sum-tree/description/?envType=daily-question&envId=2023-12-04
    def bstToGst(self, root: TreeNode) -> TreeNode:

        def dfs(node: TreeNode):
            if not node: return     # 叶子节点 返回
            dfs(node.right)         # 处理右子节点
            self.node_sum += node.val   # 累加节点和
            node.val = self.node_sum    # 更新节点值
            dfs(node.left)          # 处理左子节点
        
        self.node_sum = 0
        dfs(root)
        return root

    # 20231205
    # https://leetcode.cn/problems/minimum-fuel-cost-to-report-to-the-capital/description/?envType=daily-question&envId=2023-12-05
    def minimumFuelCost(self, roads: List[List[int]], seats: int) -> int:
        """
        * 对当前城市节点city进行深度优先搜索，返回去往首都路上经过该城市的代表人数
         @param city: 当前城市节点编号
         @param tree: 树结构，tree[i] 表示城市节点i的所有邻节点
         @param visited: 表示城市节点是否访问过
         @seats: 每辆车的容量
         @return：返回去往首都路上经过该城市的代表人数people
        """
        def dfs(city: int) -> int:
            people = 1     # 该城市本身有一个代表
            # 枚举当前城市节点的所有邻节点
            for neighbor in tree[city]:
                # 处理未遍历过的邻节点
                if not visited[neighbor]:
                    visited[neighbor] = True                 
                    people += dfs(neighbor)  # 累加到达当前城市的代表人数
            if city:
                # city不为0，就需要在移动到下一个节点，people个代表需要的车辆数等于people ÷ seats 向上取整
                # 即有这么多辆车从当前节点到达下一个节点，每辆车消耗1汽油
                self.fuel += (people + seats - 1) // seats           
            return people

        self.fuel = 0
        n = len(roads) + 1       # 节点个数
        # 生成树结构
        tree = [[] for _ in range(n)]
        for (a, b) in roads:
            tree[a].append(b)
            tree[b].append(a)
        visited = [False] * n    # 标记数组
        visited[0] = True          # 初始标记0节点已遍历
        dfs(0)   # 从0节点开始深度优先搜索寻找每一条路径
        return self.fuel

    # 20231206
    # https://leetcode.cn/problems/minimize-the-total-price-of-the-trips/description/?envType=daily-question&envId=2023-12-06
    def minimumTotalPrice(
        self, n: int, edges: List[List[int]], price: List[int], trips: List[List[int]]
    ) -> int:
        def dfs(i: int, fa: int, k: int) -> bool:
            cnt[i] += 1
            if i == k:
                return True
            ok = any(j != fa and dfs(j, i, k) for j in g[i])
            if not ok:
                cnt[i] -= 1
            return ok

        def dfs2(i: int, fa: int) -> (int, int):
            a = cnt[i] * price[i]
            b = a // 2
            for j in g[i]:
                if j != fa:
                    x, y = dfs2(j, i)
                    a += min(x, y)
                    b += x
            return a, b

        g = [[] for _ in range(n)]
        for a, b in edges:
            g[a].append(b)
            g[b].append(a)
        cnt = Counter()
        for start, end in trips:
            dfs(start, -1, end)
        return min(dfs2(0, -1))

    # 20231207
    # https://leetcode.cn/problems/reorder-routes-to-make-all-paths-lead-to-the-city-zero/description/?envType=daily-question&envId=2023-12-07
    def minReorder(self, n: int, connections: List[List[int]]) -> int:
        # 生成树结构，tree[i]中的每个元素表示{节点i的邻节点，是否由节点i指向邻节点}
        tree = [[] for _ in range(n)]
        for (a, b) in connections:
            tree[a].append((b, 1))    # a 指向 b，标记为1
            tree[b].append((a, 0))    # b 不指向 a，标记为0
        # 广度优先搜索
        visited = set()    # visited[i] 表示节点i 是否已经处理
        queue = [0]          # BFS使用的队列; 从0节点开始遍历
        visited.add(0)
        res = 0
        while queue:
            node = queue.pop(0)      # 获取队首节点
            for (neighbor, flag) in tree[node]:
                # 枚举节点node的所有邻节点
                if neighbor in visited: continue    # 跳过处理过的节点
                visited.add(neighbor)               # 标记邻节点已处理
                queue.append(neighbor)              # 邻节点入队
                if flag: res += 1                   # 如果由当前节点指向邻节点，需要反向
        return res

    # 20231208
    # https://leetcode.cn/problems/maximum-earnings-from-taxi/description/?envType=daily-question&envId=2023-12-08
    def maxTaxiEarnings(self, n: int, rides: List[List[int]]) -> int:
        groups = defaultdict(list)
        for start, end, tip in rides:
            groups[end].append((start, end - start + tip))

        f = [0] * (n + 1)
        for i in range(2, n + 1):
            f[i] = f[i - 1]
            if i in groups:
                f[i] = max(f[i], max(f[s] + t for s, t in groups[i]))
        return f[n]

    # 20231209
    # https://leetcode.cn/problems/next-greater-numerically-balanced-number/description/?envType=daily-question&envId=2023-12-09
    def nextBeautifulNumber(self, n: int) -> int:
        for x in count(n + 1):
            y = x
            cnt = [0] * 10
            while y:
                y, v = divmod(y, 10)
                cnt[v] += 1
            if all(v == 0 or i == v for i, v in enumerate(cnt)):
                return x

    # 20231210
    # https://leetcode.cn/problems/climbing-stairs/description/?envType=daily-question&envId=2023-12-10
    def climbStairs(self, n: int) -> int:
        a, b = 1, 1
        for _ in range(n - 1):
            a, b = b, a + b
        return b

    # 20231211
    # https://leetcode.cn/problems/path-with-minimum-effort/description/?envType=daily-question&envId=2023-12-11
    def minimumEffortPath(self, heights: List[List[int]]) -> int:
        # 上下左右四个方向的增量变化
        moves = [[-1,0],[1,0],[0,-1],[0,1]]
        n, m = len(heights), len(heights[0])
        # dps 是状态记录表 dps[y][x] 代表的是从 0,0 到各 y,x 最低需要多少体量
        dps = [[inf] * m for _ in range(n)]
        dps[0][0] = 0
        # 初始是 0,0 并且不需要体力
        heap = [[0,0,0]]
        while heap:
            s,y,x = heappop(heap)
            # 如果过往记录大于现在记录，整理这个记录不是最优
            if s > dps[y][x]: continue
            # 达到了终点
            if y == n - 1 and x == m - 1: break
            # 从当前位置出发 4个方向 都尝试一下需要多少体力值到达
            for addY, addX in moves:
                yy, xx = y + addY, x + addX
                if yy < 0 or yy >= n or xx < 0 or xx >= m: continue
                cs = max(abs(heights[y][x] - heights[yy][xx]), s)
                # 如果这个尝试比当前记录更低，则放入堆内，反之跳过
                if dps[yy][xx] <= cs: continue
                dps[yy][xx] = cs
                heappush(heap, [cs,yy, xx])
        return dps[n - 1][m - 1]

    # 20231212
    # https://leetcode.cn/problems/next-greater-element-iv/description/?envType=daily-question&envId=2023-12-12
    def secondGreaterElement(self, nums: List[int]) -> List[int]:
        n = len(nums)
        ans = [-1] * n     # 结果数组，初始都为-1
        st = []             # 单调栈，用于存储构成从栈底到栈顶单调递减的单调栈，以列表模拟，0为栈底，size()-1为栈顶
        temp = []           # 单调栈，用于存储构建单调栈st中被淘汰的元素，同样从栈底到栈顶单调递减
        for i, num in enumerate(nums):
            # nums[i]大于temp栈顶的元素，temp是遇到了一个比它大而被淘汰的元素，现在nums[i]又大于它，就是第二大整数
            while temp and nums[temp[-1]] < num:
                ans[temp[-1]] = num
                temp.pop()
            # 获取单调栈中比nums[i]小的元素范围
            j = len(st) - 1
            while j >= 0 and nums[st[j]] < num:
                j -= 1
            # 将[j+1, size())这部分被淘汰的元素保持原来的顺序加入temp栈顶，以保证temp也是单调递减的
            temp += st[j + 1: ]
            del st[j+1: ]   # 淘汰[j+1, size())这部分元素
            st.append(i)    # 将当前元素索引加入栈顶
        return ans   

    # 20231213
    # https://leetcode.cn/problems/lexicographically-smallest-palindrome/description/?envType=daily-question&envId=2023-12-13
    def makeSmallestPalindrome(self, s: str) -> str:
        cs = list(s)
        i, j = 0, len(s) - 1
        while i < j:
            cs[i] = cs[j] = min(cs[i], cs[j])
            i, j = i + 1, j - 1
        return "".join(cs)

    # 20231214
    # https://leetcode.cn/problems/stamping-the-grid/description/?envType=daily-question&envId=2023-12-14
    def possibleToStamp(self, grid: List[List[int]], stampHeight: int, stampWidth: int) -> bool:
        m, n = len(grid), len(grid[0])
        s = [[0] * (n + 1) for _ in range(m + 1)]
        for i, row in enumerate(grid, 1):
            for j, v in enumerate(row, 1):
                s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + v
        d = [[0] * (n + 2) for _ in range(m + 2)]
        for i in range(1, m - stampHeight + 2):
            for j in range(1, n - stampWidth + 2):
                x, y = i + stampHeight - 1, j + stampWidth - 1
                if s[x][y] - s[x][j - 1] - s[i - 1][y] + s[i - 1][j - 1] == 0:
                    d[i][j] += 1
                    d[i][y + 1] -= 1
                    d[x + 1][j] -= 1
                    d[x + 1][y + 1] += 1
        for i, row in enumerate(grid, 1):
            for j, v in enumerate(row, 1):
                d[i][j] += d[i - 1][j] + d[i][j - 1] - d[i - 1][j - 1]
                if v == 0 and d[i][j] == 0:
                    return False
        return True

    # 20231215
    # https://leetcode.cn/problems/reverse-odd-levels-of-binary-tree/submissions/489142756/?envType=daily-question&envId=2023-12-15
    def reverseOddLevels(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        q = deque([root])
        i = 0
        while q:
            if i & 1:
                l, r = 0, len(q) - 1
                while l < r:
                    q[l].val, q[r].val = q[r].val, q[l].val
                    l, r = l + 1, r - 1
            for _ in range(len(q)):
                node = q.popleft()
                if node.left:
                    q.append(node.left)
                    q.append(node.right)
            i += 1
        return root

    # 20231217
    # https://leetcode.cn/problems/min-cost-climbing-stairs/description/?envType=daily-question&envId=2023-12-17
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        n = len(cost)
        dp = [0] * n
        dp[0], dp[1] = cost[0], cost[1]
        for i in range(2, n):
            dp[i] = min(dp[i - 2], dp[i - 1]) + cost[i]
        return min(dp[-2], dp[-1])

    # 20231218
    # https://leetcode.cn/problems/find-peak-element/description/?envType=daily-question&envId=2023-12-18
    def findPeakElement(self, nums: List[int]) -> int:
        n = len(nums)
        left, right = 0, n    # 二分查找[left, right)，left初始取到数组左边界0，right初始取到数组右边界下一位n
        while left < right: 
            mid = left + ((right - left) >> 1)
            # 如果mid是一个峰值，直接返回索引
            # 同时对mid=0和mid=n-1进行处理
            # 这里必须取等号，因为数组中的数字不重复，不会有相等情况出现，而如果数组中只有一个极小值也能处理
            if nums[mid] >= (-sys.maxsize if mid == 0 else nums[mid - 1]) and nums[mid] >= (-sys.maxsize if mid == n - 1 else nums[mid + 1]):
                return mid

            # 如果mid的元素小于其左一个元素，那么在[left, mid)肯定会存在一个峰值
            # 否则mid的元素小于其右一个元素，那么在[mid+1, right)会存在一个峰值
            if mid > 0 and nums[mid] < nums[mid - 1]:
                right = mid
            else:
                left = mid + 1
                
        return - 1     # 数组中一定存在峰值，不会走到这里

    # 20131219
    # https://leetcode.cn/problems/find-a-peak-element-ii/description/?envType=daily-question&envId=2023-12-19
    def findPeakGrid(self, mat):
        startCol = 0
        endCol = len(mat[0])-1
       
        while startCol <= endCol:
            maxRow = 0
            midCol = (endCol+startCol)//2
            
            for row in range(len(mat)):
                maxRow = row if (mat[row][midCol] >= mat[maxRow][midCol]) else maxRow
            
            leftIsBig    =   midCol-1 >= startCol  and  mat[maxRow][midCol-1] > mat[maxRow][midCol]
            rightIsBig   =   midCol+1 <= endCol    and  mat[maxRow][midCol+1] > mat[maxRow][midCol]
            
            if (not leftIsBig) and (not rightIsBig):   # 我们找到了峰元素
                return [maxRow, midCol]
            elif rightIsBig:            # 如果rightisbig，那么right中有一个元素比所有元素都大
                startCol = midCol+1     # 元素在'midCol'，所以'midCol'不能有'peakPlane'
            else:                       # leftIsBig
                endCol = midCol-1
                
        return []

    # 20231220
    # https://leetcode.cn/problems/check-if-a-string-is-an-acronym-of-words/description/?envType=daily-question&envId=2023-12-20
    def isAcronym(self, words: List[str], s: str) -> bool:
        return "".join(w[0] for w in words) == s

    # 20231221
    # https://leetcode.cn/problems/beautiful-towers-ii/description/?envType=daily-question&envId=2023-12-21
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        n = len(maxHeights)
        sufSums = [0] * n   # 后缀和数组
        st = [n]            # 单调栈, 栈底为n表示后缀和边界
        sufS = 0            # 后缀和
        for i in range(n-1, -1, -1):
            while len(st) > 1 and maxHeights[i] <= maxHeights[st[-1]]:
                # 在到达栈底n之前，弹出位于当前位置右侧的小于等于当前位置最大高度的索引
                t = st.pop()   # 获取要弹出的元素
                sufS -= maxHeights[t] * (st[-1] - t)  # 后缀和减去弹出索引对应的区间包含的高度和
                
            sufS += maxHeights[i] * (st[-1] - i)      # 后缀和累加要加入的索引对应的区间包含的高度和
            sufSums[i] = sufS      # 记录后缀和
            st.append(i)             # 元素入栈
            
        st = [-1]   # 单调栈, 栈底为-1表示前缀和边界
        res = 0     # 结果值
        preS = 0    # 前缀和
        for i, h in enumerate(maxHeights):
            while len(st) > 1 and h <= maxHeights[st[-1]]:
                # 在到达栈底-1之前，弹出位于当前位置左侧的小于等于当前位置最大高度的索引
                t = st.pop()
                preS -= maxHeights[t] * (t - st[-1])  # 前缀和和减去弹出索引对应的区间包含的高度和

            preS += h * (i - st[-1])                  # 前缀和累加要加入的索引对应的区间包含的高度和
            res = max(res, preS + sufSums[i] - h)     # 得到当前位置前后缀和，更新最大值
            st.append(i)     # 元素入栈

        return res  

    # 20231222
    # https://leetcode.cn/problems/minimum-number-of-removals-to-make-mountain-array/description/?envType=daily-question&envId=2023-12-22
    def minimumMountainRemovals(self, nums: List[int]) -> int:
        n = len(nums)
        
        # 函数用于计算给定数组的LIS
        def calculate_lis(arr):
            dp = [1] * len(arr)
            for i in range(1, len(arr)):
                for j in range(i):
                    if arr[i] > arr[j]:
                        dp[i] = max(dp[i], dp[j] + 1)
            return dp
        
        # 从左和右计算每个索引的LIS
        lis_left = calculate_lis(nums)
        lis_right = calculate_lis(nums[::-1])[::-1]
        
        max_mountain = 0
        
        # 通过寻找公共元素来计算最大的山
        for i in range(1, n - 1):
            if lis_left[i] > 1 and lis_right[i] > 1:
                max_mountain = max(max_mountain, lis_left[i] + lis_right[i] - 1)
        
        # 返回所需的最小移除数
        return n - max_mountain

    # 20231223
    # https://leetcode.cn/problems/remove-stones-to-minimize-the-total/description/?envType=daily-question&envId=2023-12-23
    def minStoneSum(self, piles: List[int], k: int) -> int:
        for i in range(len(piles)):
            piles[i] *= -1  # 变成相反数，这样堆化后就是最大堆了
        heapify(piles)  # 原地堆化
        while k and piles[0]:
            heapreplace(piles, piles[0] // 2)  # 负数下取整等于正数上取整
            k -= 1
        return -sum(piles)

    # 20231224
    # https://leetcode.cn/problems/minimum-garden-perimeter-to-collect-enough-apples/description/?envType=daily-question&envId=2023-12-24
    def minimumPerimeter(self, neededApples: int) -> int:
        n = int(cbrt(neededApples / 4))
        if 2 * n * (n + 1) * (2 * n + 1) < neededApples:
            n += 1
        return 8 * n

    # 20231225
    # https://leetcode.cn/problems/number-of-burgers-with-no-waste-of-ingredients/description/?envType=daily-question&envId=2023-12-25
    def numOfBurgers(self, tomatoSlices: int, cheeseSlices: int) -> List[int]:
        # 2x = t - 2c, 2y = 4c - t，因此两个等式右侧的值必须为偶数
        # xy必须非负，即等式右边的值必须为非负
        a, b = tomatoSlices - 2 * cheeseSlices, 4 * cheeseSlices - tomatoSlices
        if a < 0 or b < 0 or a & 1 or b & 1:
            return []
        return [a >> 1, b >> 1] 

    # 20231226
    # https://leetcode.cn/problems/maximum-students-taking-exam/description/?envType=daily-question&envId=2023-12-26
    def maxStudents(self, seats: List[List[str]]) -> int:
        a = [sum((c == '.') << j for j, c in enumerate(s)) for s in seats]
        @cache
        def dfs(i: int, j: int, k: int) -> int:
            if j == 0:
                return dfs(i - 1, a[i - 1] & ~(k << 1 | k >> 1), 0) if i else 0
            lb = j & -j
            return max(dfs(i, j ^ lb, k), dfs(i, j & ~(lb * 3), k | lb) + 1)
        return dfs(len(seats) - 1, a[-1], 0)

    # 20231227
    # https://leetcode.cn/problems/determine-the-winner-of-a-bowling-game/description/?envType=daily-question&envId=2023-12-27
    def isWinner(self, player1: List[int], player2: List[int]) -> int:
        n = len(player1)
        score_player1 = 0
        score_player2 = 0

        for i in range(n):
            # 计算玩家1的得分
            if i >= 1 and (player1[i-1] == 10 or (i >= 2 and player1[i-2] == 10)):
                score_player1 += 2 * player1[i]
            else:
                score_player1 += player1[i]

            # 计算玩家2的得分
            if i >= 1 and (player2[i-1] == 10 or (i >= 2 and player2[i-2] == 10)):
                score_player2 += 2 * player2[i]
            else:
                score_player2 += player2[i]

        # 比较分数并决定获胜者
        if score_player1 > score_player2:
            return 1
        elif score_player2 > score_player1:
            return 2
        else:
            return 0

    # 20231228
    # https://leetcode.cn/problems/collecting-chocolates/description/?envType=daily-question&envId=2023-12-28
    def minCost(self, nums: List[int], x: int) -> int:
        n = len(nums)
        f = [[0] * n for _ in range(n)]
        for i, v in enumerate(nums):
            f[i][0] = v
            for j in range(1, n):
                f[i][j] = min(f[i][j - 1], nums[(i + j) % n])
        return min(sum(f[i][j] for i in range(n)) + x * j for j in range(n))

    # 20231229
    # https://leetcode.cn/problems/buy-two-chocolates/description/?envType=daily-question&envId=2023-12-29
    def buyChoco(self, prices: List[int], money: int) -> int:
        min_price, second_min_price = 101, 101   # 初始化最小价格min和次小价格second_min为一个不可能的极大值101
        for p in prices:
            if p <= min_price:
                # 如果出现小于等于最小价格的值
                # 当前的最小价格就被顺延成第二小价格，同时更新最小价格
                second_min_price = min_price
                min_price = p
            elif p < second_min_price:
                # 如果出现一个大于最小价格小于次小价格的值，那就更新次小价格
                second_min_price = p
        rest = money - min_price - second_min_price   # 计算余数
        return money if rest < 0 else rest                 # 根据余数返回值

    # 20231230
    # https://leetcode.cn/problems/day-of-the-week/description/?envType=daily-question&envId=2023-12-30
    def dayOfTheWeek(self, day: int, month: int, year: int) -> str:
        return datetime.datetime(year, month, day).strftime("%A")

    # 20231231
    # https://leetcode.cn/problems/day-of-the-year/description/?envType=daily-question&envId=2023-12-31
    def dayOfYear(self, date: str) -> int:
        return datetime.datetime.strptime(date, "%Y-%m-%d").timetuple().tm_yday

    # 20240101
    # https://leetcode.cn/problems/maximum-profit-of-operating-a-centennial-wheel/description/?envType=daily-question&envId=2024-01-01
    def minOperationsMaxProfit(self, customers: List[int], boardingCost: int, runningCost: int) -> int:
        ans = -1
        mx = t = 0
        wait = 0
        i = 0
        while wait or i < len(customers):
            wait += customers[i] if i < len(customers) else 0
            up = wait if wait < 4 else 4
            wait -= up
            t += up * boardingCost - runningCost
            i += 1
            if t > mx:
                mx = t
                ans = i
        return ans

    # 20240102
    # https://leetcode.cn/problems/count-the-repetitions/description/?envType=daily-question&envId=2024-01-02
    def getMaxRepetitions(self, s1: str, n1: int, s2: str, n2: int) -> int:
        if n1 == 0:
            return 0
        s1cnt, index, s2cnt = 0, 0, 0
        # recall 是我们用来找循环节的变量，它是一个哈希映射
        # 我们如何找循环节？假设我们遍历了 s1cnt 个 s1，此时匹配到了第 s2cnt 个 s2 中的第 index 个字符
        # 如果我们之前遍历了 s1cnt' 个 s1 时，匹配到的是第 s2cnt' 个 s2 中同样的第 index 个字符，那么就有循环节了
        # 我们用 (s1cnt', s2cnt', index) 和 (s1cnt, s2cnt, index) 表示两次包含相同 index 的匹配结果
        # 那么哈希映射中的键就是 index，值就是 (s1cnt', s2cnt') 这个二元组
        # 循环节就是；
        #    - 前 s1cnt' 个 s1 包含了 s2cnt' 个 s2
        #    - 以后的每 (s1cnt - s1cnt') 个 s1 包含了 (s2cnt - s2cnt') 个 s2
        # 那么还会剩下 (n1 - s1cnt') % (s1cnt - s1cnt') 个 s1, 我们对这些与 s2 进行暴力匹配
        # 注意 s2 要从第 index 个字符开始匹配
        recall = dict()
        while True:
            # 我们多遍历一个 s1，看看能不能找到循环节
            s1cnt += 1
            for ch in s1:
                if ch == s2[index]:
                    index += 1
                    if index == len(s2):
                        s2cnt, index = s2cnt + 1, 0
            # 还没有找到循环节，所有的 s1 就用完了
            if s1cnt == n1:
                return s2cnt // n2
            # 出现了之前的 index，表示找到了循环节
            if index in recall:
                s1cnt_prime, s2cnt_prime = recall[index]
                # 前 s1cnt' 个 s1 包含了 s2cnt' 个 s2
                pre_loop = (s1cnt_prime, s2cnt_prime)
                # 以后的每 (s1cnt - s1cnt') 个 s1 包含了 (s2cnt - s2cnt') 个 s2
                in_loop = (s1cnt - s1cnt_prime, s2cnt - s2cnt_prime)
                break
            else:
                recall[index] = (s1cnt, s2cnt)

        # ans 存储的是 S1 包含的 s2 的数量，考虑的之前的 pre_loop 和 in_loop
        ans = pre_loop[1] + (n1 - pre_loop[0]) // in_loop[0] * in_loop[1]
        # S1 的末尾还剩下一些 s1，我们暴力进行匹配
        rest = (n1 - pre_loop[0]) % in_loop[0]
        for i in range(rest):
            for ch in s1:
                if ch == s2[index]:
                    index += 1
                    if index == len(s2):
                        ans, index = ans + 1, 0
        # S1 包含 ans 个 s2，那么就包含 ans / n2 个 S2
        return ans // n2

    # 20240103
    # https://leetcode.cn/problems/remove-nodes-from-linked-list/description/?envType=daily-question&envId=2024-01-03
    def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]:
        nums = []
        while head:
            nums.append(head.val)
            head = head.next
        stk = []
        for v in nums:
            while stk and stk[-1] < v:
                stk.pop()
            stk.append(v)
        dummy = ListNode()
        head = dummy
        for v in stk:
            head.next = ListNode(v)
            head = head.next
        return dummy.next

    # 20240104
    # https://leetcode.cn/problems/maximum-rows-covered-by-columns/description/?envType=daily-question&envId=2024-01-04
    def maximumRows(self, matrix: List[List[int]], numSelect: int) -> int:
        rows = []
        for row in matrix:
            mask = reduce(or_, (1 << j for j, x in enumerate(row) if x), 0)
            rows.append(mask)

        ans = 0
        for mask in range(1 << len(matrix[0])):
            if mask.bit_count() != numSelect:
                continue
            t = sum((x & mask) == x for x in rows)
            ans = max(ans, t)
        return ans

    # 20240105
    # https://leetcode.cn/problems/number-of-visible-people-in-a-queue/description/?envType=daily-question&envId=2024-01-05
    def canSeePersonsCount(self, heights: List[int]) -> List[int]:
        n = len(heights)
        ans = [0] * n
        st = []
        for i in range(n - 1, -1, -1):
            while st and st[-1] < heights[i]:
                st.pop()
                ans[i] += 1
            if st:  # 还可以再看到一个人
                ans[i] += 1
            st.append(heights[i])
        return ans

    # 20240106
    # https://leetcode.cn/problems/insert-greatest-common-divisors-in-linked-list/description/?envType=daily-question&envId=2024-01-06
    def insertGreatestCommonDivisors(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur = head
        while cur.next:
            cur.next = ListNode(gcd(cur.val, cur.next.val), cur.next)
            cur = cur.next.next
        return head

    # 20230107
    # https://leetcode.cn/problems/ransom-note/description/?envType=daily-question&envId=2024-01-07
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        cnt = Counter(magazine)
        for c in ransomNote:
            cnt[c] -= 1
            if cnt[c] < 0:
                return False
        return True

    # 20240108
    # https://leetcode.cn/problems/number-of-boomerangs/description/?envType=daily-question&envId=2024-01-08
    def numberOfBoomerangs(self, points: List[List[int]]) -> int:
        ans = 0
        for x1, y1 in points:
            cnt = Counter()
            for x2, y2 in points:
                d2 = (x1 - x2) ** 2 + (y1 - y2) ** 2
                ans += cnt[d2] * 2
                cnt[d2] += 1
        return ans

    # 20240109
    # https://leetcode.cn/problems/extra-characters-in-a-string/description/?envType=daily-question&envId=2024-01-09
    def minExtraChar(self, s: str, dictionary: List[str]) -> int:
        d = set(dictionary)
        @cache
        def dfs(i: int) -> int:
            if i < 0: return 0
            res = dfs(i - 1) + 1  # 不选
            for j in range(i + 1):  # 枚举选哪个
                if s[j:i + 1] in d:
                    res = min(res, dfs(j - 1))
            return res
        return dfs(len(s) - 1)

    # 20240110
    # https://leetcode.cn/problems/minimum-string-length-after-removing-substrings/description/?envType=daily-question&envId=2024-01-10
    def minLength(self, s: str) -> int:
        st = []         # 利用列表模拟栈
        for ch in s:
            if st and (ch == 'B' and st[-1] == 'A' or ch == 'D' and st[-1] == 'C'):
                # 栈不为空，且当前字符与栈顶字符匹配成功，弹出栈顶字符，表示这两个字符被删除
                st.pop()
            else:
                st.append(ch)    # 入栈，等待匹配
        return len(st)          # 栈内剩下的字符个数即为删除子串后的字符数

    # 20240111
    # https://leetcode.cn/problems/minimum-additions-to-make-valid-string/description/?envType=daily-question&envId=2024-01-11
    def addMinimum(self, word: str) -> int:
        last = 'c'    # 标记前一个字符，初始为c
        adds = 0       # 插入的字符数
        for curr in word:
            adds += (ord(curr) - 1 - ord(last) + 3) % 3  # 插入的字符数，为当前字符与前一个字符的差距值 - 1
            last = curr    # 更新last
        return adds + (ord('a') + 3 - ord(last) - 1) % 3 # 最后还要校验最后一个字符之后是否需要插入字符，即认为最后一个字符为a    

    # 20240112
    # https://leetcode.cn/problems/count-common-words-with-one-occurrence/description/?envType=daily-question&envId=2024-01-12
    def countWords(self, words1: List[str], words2: List[str]) -> int:
        cnt1 = Counter(words1)
        cnt2 = Counter(words2)
        return sum(v == 1 and cnt2[w] == 1 for w, v in cnt1.items())

    # 20240113
    # https://leetcode.cn/problems/construct-string-with-repeat-limit/description/?envType=daily-question&envId=2024-01-13
    def repeatLimitedString(self, s: str, repeatLimit: int) -> str:
        cnt = [0] * 26
        for c in s:
            cnt[ord(c) - ord("a")] += 1
        ans = []
        j = 24
        for i in range(25, -1, -1):
            j = min(i - 1, j)
            while 1:
                x = min(repeatLimit, cnt[i])
                cnt[i] -= x
                ans.append(ascii_lowercase[i] * x)
                if cnt[i] == 0:
                    break
                while j >= 0 and cnt[j] == 0:
                    j -= 1
                if j < 0:
                    break
                cnt[j] -= 1
                ans.append(ascii_lowercase[j])
        return "".join(ans)

    # 20240114
    # https://leetcode.cn/problems/remove-duplicates-from-sorted-list/description/?envType=daily-question&envId=2024-01-14
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur = head
        while cur and cur.next:
            if cur.val == cur.next.val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return head

    # 20240415
    # https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/description/?envType=daily-question&envId=2024-01-15
    def deleteDuplicates(self, head):
        if not head or not head.next:
            return head
        dummy = ListNode(0)
        dummy.next = head
        pre = dummy
        cur = head
        while cur:
            # 跳过当前的重复节点，使得cur指向当前重复元素的最后一个位置
            while cur.next and cur.val == cur.next.val:
                cur = cur.next
            if pre.next == cur:
                 # pre和cur之间没有重复节点，pre后移
                pre = pre.next
            else:
                # pre->next指向cur的下一个位置（相当于跳过了当前的重复元素）
                # 但是pre不移动，仍然指向已经遍历的链表结尾
                pre.next = cur.next
            cur = cur.next
        return dummy.next

    # 20240116
    # https://leetcode.cn/problems/count-of-integers/description/?envType=daily-question&envId=2024-01-16
    def count(self, num1: str, num2: str, min_sum: int, max_sum: int) -> int:
        @cache
        def dfs(pos: int, s: int, limit: bool) -> int:
            if pos >= len(num):
                return int(min_sum <= s <= max_sum)
            up = int(num[pos]) if limit else 9
            return (
                sum(dfs(pos + 1, s + i, limit and i == up) for i in range(up + 1)) % mod
            )

        mod = 10**9 + 7
        num = num2
        a = dfs(0, 0, True)
        dfs.cache_clear()
        num = str(int(num1) - 1)
        b = dfs(0, 0, True)
        return (a - b) % mod

    # 20240117
    # https://leetcode.cn/problems/find-maximum-number-of-string-pairs/description/?envType=daily-question&envId=2024-01-17
    def maximumNumberOfStringPairs(self, words: List[str]) -> int:
        ans = 0
        seen = set()
        for s in words:
            if s[::-1] in seen:
                ans += 1  # s 和 seen 中的 s[::-1] 匹配
            else:
                seen.add(s)
        return ans

    # 20240118
    # https://leetcode.cn/problems/removing-minimum-number-of-magic-beans/description/?envType=daily-question&envId=2024-01-18
    def minimumRemoval(self, beans: List[int]) -> int:
        n = len(beans)     # 元素个数
        beans.sort()       # 对种子进行排序
        suffix_sums = [0] * (n + 1)    # 后缀和数组，suffix_sums[i]表示[i, n)的元素和，suffix_sums[n] = 0表示[n, n)的元素和不存在
        suf_sum = 0                   # 后缀和
        for i in range(n - 1, -1, -1):
            suf_sum += beans[i]                 # 先累加当前位置元素
            suffix_sums[i] = suf_sum             # 再更新后缀和   
        ans = suffix_sums[0]          # 最少数目，初始为极大值——所有元素和，表示拿走所有魔法豆
        pre_sum = 0                   # 前缀和，表示[0,i)的元素和
        for i in range(n):
            # 每次要将数组中的元素都变成beans[i]，比beans[i]小的魔法豆全都拿走，即为前缀和；
            # 比beans[i]大的魔法豆要变成beans，即为后缀和 - beans[i] * (n - i - 1)
            ans = min(ans, pre_sum + suffix_sums[i + 1] - beans[i] * (n - i - 1))
            pre_sum += beans[i]
        return ans

    # 20240119
    # https://leetcode.cn/problems/minimum-time-to-make-array-sum-at-most-x/description/?envType=daily-question&envId=2024-01-19
    def minimumTime(self, nums1: List[int], nums2: List[int], x: int) -> int:
        n = len(nums1)
        
        # 将数组按照nums2的升序排序
        pairs = sorted(zip(nums1, nums2), key=lambda x: x[1])
        
        # 计算dp数组
        dp = [[0] * (n + 1) for _ in range(n + 1)]
        for i in range(1, n + 1):
            for j in range(1, i + 1):
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + pairs[i - 1][1] * j + pairs[i - 1][0])
        
        # 寻找最小的操作次数
        for t in range(n + 1):
            if sum(nums1) + sum(nums2) * t - dp[n][t] <= x:
                return t
        return -1

    # 20240120
    # https://leetcode.cn/problems/split-strings-by-separator/description/?envType=daily-question&envId=2024-01-20
    def splitWordsBySeparator(self, words: List[str], separator: str) -> List[str]:
        return [s for w in words for s in w.split(separator) if s]

    # 20240121
    # https://leetcode.cn/problems/split-array-largest-sum/description/?envType=daily-question&envId=2024-01-21
    def splitArray(self, nums: List[int], k: int) -> int:
        def check(mx: int) -> bool:
            cnt = 1
            s = 0
            for x in nums:
                if s + x <= mx:
                    s += x
                else:  # 新划分一段
                    if cnt == k:
                        return False
                    cnt += 1
                    s = x
            return True

        right = sum(nums)
        left = max(max(nums) - 1, (right - 1) // k)
        while left + 1 < right:
            mid = (left + right) // 2
            if check(mid):
                right = mid
            else:
                left = mid
        return right

    # 20240122
    # https://leetcode.cn/problems/maximum-swap/description/?envType=daily-question&envId=2024-01-22
    def maximumSwap(self, num: int) -> int:
        s = str(num)
        max_idx = len(s) - 1
        p = q = -1
        for i in range(len(s) - 2, -1, -1):
            if s[i] > s[max_idx]:  # s[i] 是目前最大数字
                max_idx = i
            elif s[i] < s[max_idx]:  # s[i] 右边有比它大的
                p, q = i, max_idx  # 更新 p 和 q
        if p == -1:  # 这意味着 s 是降序的
            return num
        s = list(s)
        s[p], s[q] = s[q], s[p]  # 交换 s[p] 和 s[q]
        return int(''.join(s))

    # 20240123
    # https://leetcode.cn/problems/longest-alternating-subarray/description/?envType=daily-question&envId=2024-01-23
    def alternatingSubarray(self, nums: List[int]) -> int:
        ans = -1
        i, n = 0, len(nums)
        while i < n - 1:
            if nums[i + 1] - nums[i] != 1:
                i += 1  # 直接跳过
                continue
            i0 = i  # 记录这一组的开始位置
            i += 2  # i 和 i+1 已经满足要求，从 i+2 开始判断
            while i < n and nums[i] == nums[i0] + (i - i0) % 2:
                i += 1
            # 从 i0 到 i-1 是满足题目要求的（并且无法再延长的）子数组
            ans = max(ans, i - i0)
            i -= 1
        return ans 

    # 20240124
    # https://leetcode.cn/problems/beautiful-towers-i/description/?envType=daily-question&envId=2024-01-24
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        ans, n = 0, len(maxHeights)
        for i, x in enumerate(maxHeights):
            y = t = x
            for j in range(i - 1, -1, -1):
                y = min(y, maxHeights[j])
                t += y
            y = x
            for j in range(i + 1, n):
                y = min(y, maxHeights[j])
                t += y
            ans = max(ans, t)
        return ans

    # 20240125
    # https://leetcode.cn/problems/sum-of-values-at-indices-with-k-set-bits/description/?envType=daily-question&envId=2024-01-25
    def sumIndicesWithKSetBits(self, nums: List[int], k: int) -> int:
        return sum(x for i, x in enumerate(nums) if i.bit_count() == k)

    # 20240126
    # https://leetcode.cn/problems/minimum-edge-weight-equilibrium-queries-in-a-tree/description/?envType=daily-question&envId=2024-01-26
    def minOperationsQueries(self, n: int, edges: List[List[int]], queries: List[List[int]]) -> List[int]:
        g = [[] for _ in range(n)]
        for x, y, w in edges:
            g[x].append((y, w - 1))
            g[y].append((x, w - 1))

        m = n.bit_length()
        pa = [[-1] * m for _ in range(n)]
        cnt = [[[0] * 26 for _ in range(m)] for _ in range(n)]
        depth = [0] * n
        def dfs(x: int, fa: int) -> None:
            pa[x][0] = fa
            for y, w in g[x]:
                if y != fa:
                    cnt[y][0][w] = 1
                    depth[y] = depth[x] + 1
                    dfs(y, x)
        dfs(0, -1)

        # 倍增模板
        for i in range(m - 1):
            for x in range(n):
                p = pa[x][i]
                if p != -1:
                    pp = pa[p][i]
                    pa[x][i + 1] = pp
                    for j, (c1, c2) in enumerate(zip(cnt[x][i], cnt[p][i])):
                        cnt[x][i + 1][j] = c1 + c2

        ans = []
        for x, y in queries:
            path_len = depth[x] + depth[y]  # 最后减去 depth[lca] * 2
            cw = [0] * 26
            if depth[x] > depth[y]:
                x, y = y, x

            # 使 y 和 x 在同一深度
            k = depth[y] - depth[x]
            for i in range(k.bit_length()):
                if (k >> i) & 1:  # k 二进制从低到高第 i 位是 1
                    p = pa[y][i]
                    for j, c in enumerate(cnt[y][i]):
                        cw[j] += c
                    y = p

            if y != x:
                for i in range(m - 1, -1, -1):
                    px, py = pa[x][i], pa[y][i]
                    if px != py:
                        for j, (c1, c2) in enumerate(zip(cnt[x][i], cnt[y][i])):
                            cw[j] += c1 + c2
                        x, y = px, py  # 同时上跳 2^i 步
                for j, (c1, c2) in enumerate(zip(cnt[x][0], cnt[y][0])):
                    cw[j] += c1 + c2
                x = pa[x][0]

            lca = x
            path_len -= depth[lca] * 2
            ans.append(path_len - max(cw))
        return ans

    # 20240127
    # https://leetcode.cn/problems/maximum-number-of-alloys/description/?envType=daily-question&envId=2024-01-27
    def maxNumberOfAlloys(self, n: int, k: int, budget: int, composition: List[List[int]], stock: List[int], cost: List[int]) -> int:
        ans = 0
        mx = min(stock) + budget
        for comp in composition:
            def check(num: int) -> bool:
                money = 0
                for s, base, c in zip(stock, comp, cost):
                    if s < base * num:
                        money += (base * num - s) * c
                        if money > budget:
                            return False
                return True

            left, right = ans, mx + 1
            while left + 1 < right:  # 开区间写法
                mid = (left + right) // 2
                if check(mid):
                    left = mid
                else:
                    right = mid
            ans = left
        return ans

    # 20240128
    # https://leetcode.cn/problems/water-and-jug-problem/description/?envType=daily-question&envId=2024-01-28
    def canMeasureWater(self, x: int, y: int, z: int) -> bool:
        def dfs(i: int, j: int) -> bool:
            if (i, j) in vis:
                return False
            vis.add((i, j))
            if i == z or j == z or i + j == z:
                return True
            if dfs(x, j) or dfs(i, y) or dfs(0, j) or dfs(i, 0):
                return True
            a = min(i, y - j)
            b = min(j, x - i)
            return dfs(i - a, j + a) or dfs(i + b, j - b)

        vis = set()
        return dfs(0, 0)

    # 20240129
    # https://leetcode.cn/problems/freedom-trail/?envType=daily-question&envId=2024-01-29
    def findRotateSteps(self, ring: str, key: str) -> int:
        dic = defaultdict(list)
        for i, c in enumerate(ring):
            dic[c].append(i)
        n, k = len(ring), len(key)
        @cache
        def dfs(idx, cur):
            if cur == k:
                return 0
            ls = dic[key[cur]]
            ans = inf
            for i in ls:
                ans = min(ans, min(abs(i - idx), n - abs(i - idx)) + 1 + dfs(i, cur + 1))
            return ans

        return dfs(0, 0)

    # 20240130
    # https://leetcode.cn/problems/minimum-seconds-to-equalize-a-circular-array/description/?envType=daily-question&envId=2024-01-30
    def minimumSeconds(self, nums: List[int]) -> int:
        num_idxs = {}    # 存储每个元素出现的位置
        n = len(nums)
        for i, num in enumerate(nums):
            if num not in num_idxs:
                num_idxs[num] = []
            num_idxs[nums[i]].append(i)

        res = n + 1    # 初始为一个极大值，数组长度+1
        for _, idxes in num_idxs.items():
            need_seconds = 0    # 元素it.first覆盖整个数组需要的时间
            idxes.append(idxes[0] + n)   # 将首个位置的索引+n，计算首个位置和最后一个位置之间的环形巨鹿
            for i in range(1, len(idxes)):
                need_seconds = max(need_seconds, (idxes[i] - idxes[i-1]) // 2)  # 统计每两个元素之间需要覆盖的时间，取最大覆盖时间
            res = min(res, need_seconds)
        return res

    # 20240131
    # https://leetcode.cn/problems/find-the-distinct-difference-array/description/?envType=daily-question&envId=2024-01-31
    def distinctDifferenceArray(self, nums: List[int]) -> List[int]:
        n = len(nums)
        diff = [0] * n    # 结果数组
        set_ = set()      # 存储出现的不同元素
        
        # 先统计前缀和，注意前缀和是范围[0, i]，因此先添加nums[i]，再统计不同元素数目
        for i, num in enumerate(nums):
            set_.add(num)
            diff[i] = len(set_)
        set_.clear()    # 清空哈希表
        # 再统计后缀和，注意后缀和是范围[i+1 n)，因此先统计不同元素数目，再添加nums[i]
        for i in range(n-1, -1, -1):
            diff[i] -= len(set_)  # diff[i]存储的是[0,i]的前缀和，直接做差就得到结果
            set_.add(nums[i])
        
        return diff

    # 20240201
    # https://leetcode.cn/problems/5TxKeK/description/?envType=daily-question&envId=2024-02-01
    def numsGame(self, nums: List[int]) -> List[int]:
        MOD = 1_000_000_007
        ans = [0] * len(nums)
        left = []   # 维护较小的一半，大根堆（小根堆取负号）
        right = []  # 维护较大的一半，小根堆
        left_sum = right_sum = 0
        for i, b in enumerate(nums):
            b -= i
            if i % 2 == 0:  # 前缀长度是奇数
                left_sum -= max(-left[0] - b, 0) if left else 0
                t = -heappushpop(left, -b)
                right_sum += t
                heappush(right, t)
                ans[i] = (right_sum - right[0] - left_sum) % MOD
            else:  # 前缀长度是偶数
                right_sum += max(b - right[0], 0)
                t = heappushpop(right, b)
                left_sum += t
                heappush(left, -t)
                ans[i] = (right_sum - left_sum) % MOD
        return ans

    # 20240202
    # https://leetcode.cn/problems/stone-game-vi/description/?envType=daily-question&envId=2024-02-02
    def stoneGameVI(self, aliceValues: List[int], bobValues: List[int]) -> int:
        n = len(aliceValues)
        diff = [[-(x+y), z] for (x, y, z) in zip(aliceValues, bobValues, range(n))]
        diff.sort(key=lambda x: x[0])
        a_scores = 0
        b_scores = 0
        for i in range(n):
            if i % 2 == 0:
                a_scores += aliceValues[diff[i][1]]
            else:
                b_scores += bobValues[diff[i][1]]
        if a_scores > b_scores:
            return 1
        elif a_scores == b_scores:
            return 0
        else:
            return -1

    # 20240203
    # https://leetcode.cn/problems/stone-game-vii/description/?envType=daily-question&envId=2024-02-03
    def stoneGameVII(self, stones: List[int]) -> int:
        n = len(stones)
        dp = [[0] * n for _ in range(n)]
        if not n & 1:
            for i in range(n):
                dp[i][i] = stones[i]
        for L in range(2, n + 1):
            for i in range(n - L + 1):
                j = i + L - 1
                if L & 1 == n & 1:
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
                else:
                    dp[i][j] = min(dp[i + 1][j] + stones[i], dp[i][j - 1] + stones[j])
        return dp[0][-1]

    # 20240204
    # https://leetcode.cn/problems/nim-game/description/?envType=daily-question&envId=2024-02-04
    def canWinNim(self, n: int) -> bool:
        return n % 4 != 0

    # 20240205
    # https://leetcode.cn/problems/jump-game-vi/description/?envType=daily-question&envId=2024-02-05
    def maxResult(self, nums: List[int], k: int) -> int:
        q = deque([0])
        for i in range(1, len(nums)):
            # 1. 出
            if q[0] < i - k:
                q.popleft()
            # 2. 转移
            nums[i] += nums[q[0]]
            # 3. 入
            while q and nums[i] >= nums[q[-1]]:
                q.pop()
            q.append(i)
        return nums[-1]

    # 20240206
    # https://leetcode.cn/problems/p0NxJO/description/?envType=daily-question&envId=2024-02-06
    def magicTower(self, nums: List[int]) -> int:
        if sum(nums) < 0:
            return -1
        ans = 0
        hp = 1
        h = []
        for x in nums:
            if x < 0:
                heappush(h, x)
            hp += x
            if hp < 1:
                # 这意味着 x < 0，所以前面必然会把 x 入堆
                # 所以堆必然不是空的，并且堆顶 <= x
                hp -= heappop(h)  # 反悔
                ans += 1
        return ans

    # 20240207
    # https://leetcode.cn/problems/cousins-in-binary-tree-ii/description/?envType=daily-question&envId=2024-02-07
    def replaceValueInTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        q = [root]
        root.val = 0
        while len(q) > 0:
            q2 = []
            sum = 0
            for fa in q:
                if fa.left:
                    q2.append(fa.left)
                    sum += fa.left.val
                if fa.right:
                    q2.append(fa.right)
                    sum += fa.right.val
            for fa in q:
                childSum = (fa.left.val if fa.left else 0) + (fa.right.val if fa.right else 0)
                if fa.left:
                    fa.left.val = sum - childSum
                if fa.right:
                    fa.right.val = sum - childSum
            q = q2
        return root

    # 20240208
    # https://leetcode.cn/problems/cousins-in-binary-tree/description/?envType=daily-question&envId=2024-02-08
    def isCousins(self, root: TreeNode, x: int, y: int) -> bool:
        x_depth, x_parent, y_depth, y_parent = None, None, None, None

        # 标准dfs模板, 只是在中间加了一些判断逻辑
        def dfs(root, parent, x, y, depth):
            nonlocal x_depth, x_parent, y_depth, y_parent
            if root is None:
                return 
            
            # 判断 x, y 是否等于当前节点的值, 是的话更新 x 或者 y 的深度和parent
            if root.val == x:
                x_depth = depth 
                x_parent = parent 
            if root.val == y:
                y_depth = depth
                y_parent = parent

            dfs(root.left, root, x, y, depth+1)
            dfs(root.right, root, x, y, depth+1)

        dfs(root, None, x, y, 0)
        # 最后保证 x, y的深度一样, 但是parent节点不一样, 这样才是堂兄弟
        return x_depth == y_depth and x_parent != y_parent

    # 20240209
    # https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/?envType=daily-question&envId=2024-02-09
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or root == p or root == q: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left and not right: return # 1.
        if not left: return right # 3.
        if not right: return left # 4.
        return root # 2. if left and right:

    # 20240210
    # https://leetcode.cn/problems/binary-tree-inorder-traversal/description/?envType=daily-question&envId=2024-02-10
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ans, stk = [], []
        while root or stk:
            if root:
                stk.append(root)
                root = root.left
            else:
                root = stk.pop()
                ans.append(root.val)
                root = root.right
        return ans

    # 20240211
    # https://leetcode.cn/problems/binary-tree-preorder-traversal/description/?envType=daily-question&envId=2024-02-11
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        def dfs(root):
            if root is None:
                return
            ans.append(root.val)
            dfs(root.left)
            dfs(root.right)

        ans = []
        dfs(root)
        return ans

    # 20240212
    # https://leetcode.cn/problems/binary-tree-postorder-traversal/description/?envType=daily-question&envId=2024-02-12
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        def dfs(root):
            if root is None:
                return
            dfs(root.left)
            dfs(root.right)
            ans.append(root.val)

        ans = []
        dfs(root)
        return ans

    # 20240213
    # https://leetcode.cn/problems/vertical-order-traversal-of-a-binary-tree/description/?envType=daily-question&envId=2024-02-13
    def verticalTraversal(self, root: Optional[TreeNode]) -> List[List[int]]:
        groups = defaultdict(list)
        def dfs(node: Optional[TreeNode], row: int, col: int):
            if node is None:
                return
            groups[col].append((row, node.val))  # col 相同的分到同一组
            dfs(node.left, row + 1, col - 1)
            dfs(node.right, row + 1, col + 1)
        dfs(root, 0, 0)

        ans = []
        for _, g in sorted(groups.items()):
            g.sort()  # 按照 row 排序，row 相同按照 val 排序
            ans.append([val for _, val in g])
        return ans


    # 20240214
    # https://leetcode.cn/problems/binary-tree-level-order-traversal/description/?envType=daily-question&envId=2024-02-14
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root: return []
        res, queue = [], collections.deque()
        queue.append(root)
        while queue:
            tmp = []
            for _ in range(len(queue)):
                node = queue.popleft()
                tmp.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            res.append(tmp)
        return res

    # 20240215
    # https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/description/?envType=daily-question&envId=2024-02-15
    def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
        if root is None:
            return []
        ans = []
        cur = [root]
        while cur:
            nxt = []
            vals = []
            for node in cur:
                vals.append(node.val)
                if node.left:  nxt.append(node.left)
                if node.right: nxt.append(node.right)
            cur = nxt
            ans.append(vals)
        return ans[::-1]

    # 20240216
    # https://leetcode.cn/problems/binary-tree-zigzag-level-order-traversal/description/?envType=daily-question&envId=2024-02-16
    def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root: return []
        res, deque = [], collections.deque([root])
        while deque:
            tmp = collections.deque()
            for _ in range(len(deque)):
                node = deque.popleft()
                if len(res) % 2 == 0: tmp.append(node.val) # 奇数层 -> 插入队列尾部
                else: tmp.appendleft(node.val) # 偶数层 -> 插入队列头部
                if node.left: deque.append(node.left)
                if node.right: deque.append(node.right)
            res.append(list(tmp))
        return res

    # 20240217
    # https://leetcode.cn/problems/n-ary-tree-level-order-traversal/description/?envType=daily-question&envId=2024-02-17
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        if root is None:
            return []
        ans = []
        q = [root]
        while q:
            ans.append([node.val for node in q])
            q = [c for node in q for c in node.children]
        return ans

    # 20240218
    # https://leetcode.cn/problems/n-ary-tree-preorder-traversal/description/?envType=daily-question&envId=2024-02-18
    def preorder(self, root: 'Node') -> List[int]:
        ans = []
        def dfs(node: 'Node') -> None:
            if node is None:
                return
            ans.append(node.val)
            for c in node.children:
                dfs(c)
        dfs(root)
        return ans

    # 20240219
    # https://leetcode.cn/problems/n-ary-tree-postorder-traversal/description/?envType=daily-question&envId=2024-02-19
    def postorder(self, root: 'Node') -> List[int]:
        ans = []
        def dfs(node: 'Node') -> None:
            if node is None:
                return
            for c in node.children:
                dfs(c)
            ans.append(node.val)
        dfs(root)
        return ans

    # 20240220
    # https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/?envType=daily-question&envId=2024-02-20
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def recur(root, left, right):
            if left > right: return                               # 递归终止
            node = TreeNode(preorder[root])                       # 建立根节点
            i = dic[preorder[root]]                               # 划分根节点、左子树、右子树
            node.left = recur(root + 1, left, i - 1)              # 开启左子树递归
            node.right = recur(i - left + root + 1, i + 1, right) # 开启右子树递归
            return node                                           # 回溯返回根节点

        dic, preorder = {}, preorder
        for i in range(len(inorder)):
            dic[inorder[i]] = i
        return recur(0, 0, len(inorder) - 1)

    # 20240221
    # https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/description/?envType=daily-question&envId=2024-02-21
    def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:

        h = {v: i for i, v in enumerate(inorder)}

        def f(r, R, s):
            if s == 0:
                return None

            v = postorder[R]
            node = TreeNode(v)
            i = h[v]
            node.left = f(i - 1, R - r + i - 1, s - r + i - 1)
            node.right = f(r, R - 1, r - i)
            return node

        return f(len(inorder) - 1, len(inorder) - 1, len(inorder))

    # 20240223
    # https://leetcode.cn/problems/kth-largest-sum-in-a-binary-tree/description/?envType=daily-question&envId=2024-02-23
    def kthLargestLevelSum(self, root: Optional[TreeNode], k: int) -> int:
        a = []
        q = [root]
        while q:
            s = 0
            tmp = q
            q = []
            for node in tmp:
                s += node.val
                if node.left:  q.append(node.left)
                if node.right: q.append(node.right)
            a.append(s)
        if k > len(a):
            return -1
        a.sort()
        return a[-k]

    # 20240224
    # https://leetcode.cn/problems/closest-nodes-queries-in-a-binary-search-tree/description/?envType=daily-question&envId=2024-02-24
    def closestNodes(self, root: Optional[TreeNode], queries: List[int]) -> List[List[int]]:
        a = []
        def dfs(node: Optional[TreeNode]) -> None:
            if node is None:
                return
            dfs(node.left)
            a.append(node.val)
            dfs(node.right)
        dfs(root)

        n = len(a)
        ans = []
        for q in queries:
            j = bisect_left(a, q)
            mx = a[j] if j < n else -1
            if j == n or a[j] != q:  # a[j]>q, a[j-1]<q
                j -= 1
            mn = a[j] if j >= 0 else -1
            ans.append([mn, mx])
        return ans
    
    # 20240225
    # https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/description/?envType=daily-question&envId=2024-02-25
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        def getPath(root: TreeNode, target: TreeNode) -> List[TreeNode]:
            path = list()
            node = root
            while node != target:
                path.append(node)
                if target.val < node.val:
                    node = node.left
                else:
                    node = node.right
            path.append(node)
            return path
        
        path_p = getPath(root, p)
        path_q = getPath(root, q)
        ancestor = None
        for u, v in zip(path_p, path_q):
            if u == v:
                ancestor = u
            else:
                break
        
        return ancestor

    # 20240226
    # https://leetcode.cn/problems/range-sum-of-bst/description/?envType=daily-question&envId=2024-02-26
    def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int:
        if root is None:
            return 0
        x = root.val
        if x > high:  # 右子树没有节点在范围内，只需递归左子树
            return self.rangeSumBST(root.left, low, high)
        if x < low:  # 左子树没有节点在范围内，只需递归右子树
            return self.rangeSumBST(root.right, low, high)
        return x + self.rangeSumBST(root.left, low, high) + \
                   self.rangeSumBST(root.right, low, high)

    # 20240227
    # https://leetcode.cn/problems/count-valid-paths-in-a-tree/description/?envType=daily-question&envId=2024-02-27
    def countPaths(self, n: int, edges: List[List[int]]) -> int:
        MX = 10 ** 5 + 1
        primes = []
        is_prime = [True] * MX
        for i in range(2, MX):
            if is_prime[i]:
                primes.append(i)
                for j in range(i * i, MX, i):
                    is_prime[j] = False
        st = set(primes)

        # 建图
        g = [[] for _ in range(n+1)]
        for x, y in edges:
            g[x].append(y)
            g[y].append(x)

        # 并查集 fa(只管非质数就行)
        fa = list(range(n+1))

        def find(x: int) -> int:
            if fa[x] != x:
                fa[x] = find(fa[x])
            return fa[x]

        size = [1] * (n + 1)
        ans = 0
        for x in range(1, n+1):
            if x not in st:
                fx = find(x)
                for y in g[x]:
                    if y not in st:
                        fy = find(y)
                        if fy == fx:
                            continue
                        size[fx] += size[fy]
                        fa[fy] = fx
        for x in range(1, n+1):
            if x in st:
                res = 0
                cur = 0
                for y in g[x]:
                    if y not in st:
                        fy = find(y)
                        res += size[fy]
                        cur += size[fy]* size[fy]
                ans += res + (res * res  - cur)//2
        return ans

    # 20240228
    # https://leetcode.cn/problems/make-costs-of-paths-equal-in-a-binary-tree/description/?envType=daily-question&envId=2024-02-28
    def minIncrements(self, n: int, cost: List[int]) -> int:
        ans = 0
        for i in range(n // 2, 0, -1):  # 从最后一个非叶节点开始算
            ans += abs(cost[i * 2 - 1] - cost[i * 2])  # 两个子节点变成一样的
            cost[i - 1] += max(cost[i * 2 - 1], cost[i * 2])  # 累加路径和
        return ans

    # 20240229
    # https://leetcode.cn/problems/count-number-of-possible-root-nodes/description/?envType=daily-question&envId=2024-02-29
    def rootCount(self, edges: List[List[int]], guesses: List[List[int]], k: int) -> int:
        g = [[] for _ in range(len(edges) + 1)]
        for x, y in edges:
            g[x].append(y)
            g[y].append(x)  # 建图

        s = {(x, y) for x, y in guesses}  # guesses 转成哈希表 s

        ans = cnt0 = 0
        def dfs(x: int, fa: int) -> None:
            nonlocal cnt0
            for y in g[x]:
                if y != fa:
                    cnt0 += (x, y) in s  # 以 0 为根时，猜对了
                    dfs(y, x)
        dfs(0, -1)

        def reroot(x: int, fa: int, cnt: int) -> None:
            nonlocal ans
            ans += cnt >= k  # 此时 cnt 就是以 x 为根时的猜对次数
            for y in g[x]:
                if y != fa:
                    reroot(y, x, cnt - ((x, y) in s) + ((y, x) in s))
        reroot(0, -1, cnt0)
        return ans

    # 20240301
    # https://leetcode.cn/problems/check-if-there-is-a-valid-partition-for-the-array/description/?envType=daily-question&envId=2024-03-01
    def validPartition(self, nums: List[int]) -> bool:
        n = len(nums)
        dp = [False] * (n + 1)
        dp[0] = True
        for i in range(1, n + 1):
            if i >= 2:
                dp[i] = dp[i - 2] and self.validTwo(nums[i - 2], nums[i - 1])
            if i >= 3:
                dp[i] = dp[i] or (dp[i - 3] and self.validThree(nums[i - 3], nums[i - 2], nums[i - 1]))
        return dp[-1]
    
    def validTwo(self, num1: int, num2: int) -> bool:
        return num1 == num2
    
    def validThree(self, num1: int, num2: int, num3: int) -> bool:
        return (num1 == num2 == num3) or (num1 + 2 == num2 + 1 == num3)

    # 20240302
    # https://leetcode.cn/problems/reachable-nodes-with-restrictions/description/?envType=daily-question&envId=2024-03-02
    def reachableNodes(self, n: int, edges: List[List[int]], restricted: List[int]) -> int:
        r = set(restricted)
        g = [[] for _ in range(n)]
        for x, y in edges:
            if x not in r and y not in r:
                g[x].append(y)  # 都不受限才连边
                g[y].append(x)
        def dfs(x: int, fa: int) -> int:
            cnt = 1
            for y in g[x]:
                if y != fa:
                    cnt += dfs(y, x)
            return cnt
        return dfs(0, -1)

    # 20240305
    # https://leetcode.cn/problems/number-of-ways-to-arrive-at-destination/description/?envType=daily-question&envId=2024-03-05
    def countPaths(self, n: int, roads: List[List[int]]) -> int:
        g = [[inf for _ in range(n)] for _ in range(n)]  # 邻接矩阵
        for x, y, d in roads:
            g[x][y] = d
            g[y][x] = d

        dis = [inf] * n
        dis[0] = 0
        f = [0] * n
        f[0] = 1
        done = [False] * n
        while True:
            x = -1
            for i, ok in enumerate(done):
                if not ok and (x < 0 or dis[i] < dis[x]):
                    x = i
            if x == n - 1:
                # 不可能找到比 dis[-1] 更短，或者一样短的最短路了（注意本题边权都是正数）
                return f[-1]
            done[x] = True  # 最短路长度已确定（无法变得更小）
            dx = dis[x]
            for y, d in enumerate(g[x]):  # 尝试更新 x 的邻居的最短路
                new_dis = dx + d
                if new_dis < dis[y]:
                    # 就目前来说，最短路必须经过 x
                    dis[y] = new_dis
                    f[y] = f[x]
                elif new_dis == dis[y]:
                    # 和之前求的最短路一样长
                    f[y] = (f[y] + f[x]) % 1_000_000_007

    # 20240306
    # https://leetcode.cn/problems/find-the-k-or-of-an-array/description/?envType=daily-question&envId=2024-03-06
    def findKOr(self, nums: List[int], k: int) -> int:
        ans = 0
        for i in range(31):
            cnt1 = sum(x >> i & 1 for x in nums)
            if cnt1 >= k:
                ans |= 1 << i
        return ans

    # 20240307
    # https://leetcode.cn/problems/find-the-divisibility-array-of-a-string/description/?envType=daily-question&envId=2024-03-07
    def divisibilityArray(self, word: str, m: int) -> List[int]:
        ans = []
        x = 0
        for d in map(int, word):
            x = (x * 10 + d) % m
            ans.append(0 if x else 1)
        return ans+

    # 20240308
    # https://leetcode.cn/problems/find-the-minimum-possible-sum-of-a-beautiful-array/description/?envType=daily-question&envId=2024-03-08
    def minimumPossibleSum(self, n: int, k: int) -> int:
        m = min(k // 2, n)
        return (m * (m + 1) + (k * 2 + n - m - 1) * (n - m)) // 2 % 1_000_000_007

    # 20240309
    # https://leetcode.cn/problems/find-the-k-sum-of-an-array/description/?envType=daily-question&envId=2024-03-09
    def kSum(self, nums: List[int], k: int) -> int:
        s = 0
        for i, x in enumerate(nums):
            if x >= 0:
                s += x
            else:
                nums[i] = -x
        nums.sort()

        def check(sum_limit: int) -> bool:
            cnt = 1  # 空子序列算一个
            def dfs(i: int, s: int) -> None:
                nonlocal cnt
                if cnt == k or i == len(nums) or s + nums[i] > sum_limit:
                    return
                cnt += 1  # s + nums[i] <= sum_limit
                dfs(i + 1, s + nums[i])  # 选
                dfs(i + 1, s)  # 不选
            dfs(0, 0)
            return cnt == k  # 找到 k 个元素和不超过 sum_limit 的子序列
        return s - bisect_left(range(sum(nums)), True, key=check)

    # 20240310
    # https://leetcode.cn/problems/bulls-and-cows/description/?envType=daily-question&envId=2024-03-10
    def getHint(self, secret: str, guess: str) -> str:
        a = 0
        cnt_s = Counter()
        cnt_g = Counter()
        for x, y in zip(secret, guess):
            if x == y:
                a += 1
            else:
                cnt_s[x] += 1
                cnt_g[y] += 1
        b = (cnt_s & cnt_g).total()
        return f"{a}A{b}B"

    # 20240311
    # https://leetcode.cn/problems/capitalize-the-title/description/?envType=daily-question&envId=2024-03-11
    def capitalizeTitle(self, title: str) -> str:
        f = lambda s: s.title() if len(s) > 2 else s.lower()
        return ' '.join(map(f, title.split()))

    # 20240313
    # https://leetcode.cn/problems/maximum-odd-binary-number/description/?envType=daily-question&envId=2024-03-13
    def maximumOddBinaryNumber(self, s: str) -> str:
        cnt1 = s.count('1')
        return '1' * (cnt1 - 1) + '0' * (len(s) - cnt1) + '1'

    # 20240314
    # https://leetcode.cn/problems/largest-element-in-an-array-after-merge-operations/description/?envType=daily-question&envId=2024-03-14
    def maxArrayValue(self, nums: List[int]) -> int:
        for i in range(len(nums) - 2, -1, -1):
            if nums[i] <= nums[i + 1]:
                nums[i] += nums[i + 1]
        return max(nums)

    # 20240315
    # https://leetcode.cn/problems/selling-pieces-of-wood/description/?envType=daily-question&envId=2024-03-15
    def sellingWood(self, m: int, n: int, prices: List[List[int]]) -> int:
        f = [[0] * (n + 1) for _ in range(m + 1)]
        for h, w, p in prices:
            f[h][w] = p
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                f[i][j] = max(f[i][j],
                              max((f[i][k] + f[i][j - k] for k in range(1, j // 2 + 1)), default=0),  # 垂直切割
                              max((f[k][j] + f[i - k][j] for k in range(1, i // 2 + 1)), default=0))  # 水平切割
        return f[m][n]

    # 20240316
    # https://leetcode.cn/problems/maximum-number-of-moves-in-a-grid/description/?envType=daily-question&envId=2024-03-16
    def maxMoves(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        ans = 0
        def dfs(i: int, j: int) -> None:
            nonlocal ans
            ans = max(ans, j)
            if ans == n - 1:  # ans 已达到最大值
                return
            for k in i - 1, i, i + 1:  # 向右上/右/右下走一步
                if 0 <= k < m and grid[k][j + 1] > grid[i][j]:
                    dfs(k, j + 1)
            grid[i][j] = 0
        for i in range(m):
            dfs(i, 0)  # 从第一列的任一单元格出发
        return ans

    # 20240317
    # https://leetcode.cn/problems/minimum-height-trees/description/?envType=daily-question&envId=2024-03-17
    def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]:
        if n == 1:
            return [0]
        g = [[] for _ in range(n)]
        degree = [0] * n
        for a, b in edges:
            g[a].append(b)
            g[b].append(a)
            degree[a] += 1
            degree[b] += 1
        q = deque(i for i in range(n) if degree[i] == 1)
        ans = []
        while q:
            ans.clear()
            for _ in range(len(q)):
                a = q.popleft()
                ans.append(a)
                for b in g[a]:
                    degree[b] -= 1
                    if degree[b] == 1:
                        q.append(b)
        return ans

    # 20240319
    # https://leetcode.cn/problems/maximum-score-of-a-good-subarray/description/?envType=daily-question&envId=2024-03-19
    def maximumScore(self, nums: List[int], k: int) -> int:
        n = len(nums)
        left = [-1] * n
        st = []
        for i, x in enumerate(nums):
            while st and x <= nums[st[-1]]:
                st.pop()
            if st:
                left[i] = st[-1]
            st.append(i)

        right = [n] * n
        st.clear()
        for i in range(n - 1, -1, -1):
            x = nums[i]
            while st and x <= nums[st[-1]]:
                st.pop()
            if st:
                right[i] = st[-1]
            st.append(i)

        ans = 0
        for h, l, r in zip(nums, left, right):
            if l < k < r:  # 相比 84 题多了这一行
                ans = max(ans, h * (r - l - 1))
        return ans
    
    # 20240320
    # https://leetcode.cn/problems/minimum-non-zero-product-of-the-array-elements/description/?envType=daily-question&envId=2024-03-20
    def minNonZeroProduct(self, p: int) -> int:
        if p == 1:
            return 1
        mod = 10**9 + 7
        return  pow(2 ** p - 2, 2 ** (p - 1) - 1, mod) * (2 ** p - 1) % mod

    # 20240322
    # https://leetcode.cn/problems/minimum-number-of-visited-cells-in-a-grid/description/?envType=daily-question&envId=2024-03-22
    def minimumVisitedCells(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        col_stacks = [[] for _ in range(n)]  # 每列的单调栈
        for i in range(m - 1, -1, -1):
            row_st = []  # 当前行的单调栈
            for j in range(n - 1, -1, -1):
                g = grid[i][j]
                col_st = col_stacks[j]
                mn = inf if i < m - 1 or j < n - 1 else 1
                if g:
                    # 在单调栈上二分
                    k = bisect_left(row_st, -(j + g), key=lambda p: p[1])
                    if k < len(row_st):
                        mn = row_st[k][0] + 1
                    k = bisect_left(col_st, -(i + g), key=lambda p: p[1])
                    if k < len(col_st):
                        mn = min(mn, col_st[k][0] + 1)

                if mn < inf:
                    # 插入单调栈
                    while row_st and mn <= row_st[-1][0]:
                        row_st.pop()
                    row_st.append((mn, -j))  # 保证下标单调递增，方便调用 bisect_left
                    while col_st and mn <= col_st[-1][0]:
                        col_st.pop()
                    col_st.append((mn, -i))  # 保证下标单调递增，方便调用 bisect_left
        return mn if mn < inf else -1  # 最后一个算出的 mn 就是 f[0][0]

    # 20240323
    # https://leetcode.cn/problems/count-distinct-numbers-on-board/description/?envType=daily-question&envId=2024-03-23
    def distinctIntegers(self, n: int) -> int:
        return n - 1 if n > 1 else 1  # max(n - 1, 1)

    # 20240324
    # https://leetcode.cn/problems/coin-change/description/?envType=daily-question&envId=2024-03-24
    def coinChange(self, coins: List[int], amount: int) -> int:
        @cache
        def dfs(i, c):
            if i < 0:
                return 0 if c == 0 else inf
            if c < coins[i]:
                return dfs(i - 1, c)
            return min(dfs(i - 1, c), dfs(i, c - coins[i]) + 1)
        ans = dfs(len(coins) - 1, amount)
        return ans if ans < inf else -1

    # 20240325
    # https://leetcode.cn/problems/coin-change-ii/description/?envType=daily-question&envId=2024-03-25
    def change(self, amount: int, coins: List[int]) -> int:
        @cache
        def dfs(i: int, c: int) -> int:
            if i < 0:
                return 1 if c == 0 else 0
            if c < coins[i]:
                return dfs(i - 1, c)
            return dfs(i - 1, c) + dfs(i, c - coins[i])
        return dfs(len(coins) - 1, amount)

    # 20240327
    # https://leetcode.cn/problems/count-ways-to-group-overlapping-ranges/description/?envType=daily-question&envId=2024-03-27
    def countWays(self, ranges: List[List[int]]) -> int:
        ranges.sort(key=lambda p: p[0])
        m, max_r = 0, -1
        for l, r in ranges:
            if l > max_r:  # 无法合并
                m += 1  # 新区间
            max_r = max(max_r, r)  # 合并
        return pow(2, m, 1_000_000_007)
    
    # 20240328
    # https://leetcode.cn/problems/first-day-where-you-have-been-in-all-the-rooms/description/?envType=daily-question&envId=2024-03-28
    def firstDayBeenInAllRooms(self, nextVisit: List[int]) -> int:
        s = [0] * len(nextVisit)
        for i, j in enumerate(nextVisit[:-1]):
            s[i + 1] = (s[i] * 2 - s[j] + 2) % 1_000_000_007
        return s[-1]

    # 20240329
    # https://leetcode.cn/problems/minimum-sum-of-mountain-triplets-i/description/?envType=daily-question&envId=2024-03-29
    def minimumSum(self, nums: List[int]) -> int:
        n = len(nums)
        suf = [0] * n
        suf[-1] = nums[-1]  # 后缀最小值
        for i in range(n - 2, 1, -1):
            suf[i] = min(suf[i + 1], nums[i])

        ans = inf
        pre = nums[0]  # 前缀最小值
        for j in range(1, n - 1):
            if pre < nums[j] > suf[j + 1]:  # 山形
                ans = min(ans, pre + nums[j] + suf[j + 1])  # 更新答案
            pre = min(pre, nums[j])
        return ans if ans < inf else -1

    # 20240330
    # https://leetcode.cn/problems/minimum-number-of-coins-to-be-added/description/?envType=daily-question&envId=2024-03-30
    def minimumAddedCoins(self, coins: List[int], target: int) -> int:
        coins.sort()
        ans, s, i = 0, 1, 0
        while s <= target:
            if i < len(coins) and coins[i] <= s:
                s += coins[i]
                i += 1
            else:
                s *= 2  # 必须添加 s
                ans += 1
        return ans

    # 20240331
    # https://leetcode.cn/problems/verify-preorder-serialization-of-a-binary-tree/description/?envType=daily-question&envId=2024-03-31
    def isValidSerialization(self, preorder):
        stack = []
        for node in preorder.split(','):
            stack.append(node)
            while len(stack) >= 3 and stack[-1] == stack[-2] == '#' and stack[-3] != '#':
                stack.pop(), stack.pop(), stack.pop()
                stack.append('#')
        return len(stack) == 1 and stack.pop() == '#'

    # 20240401
    # https://leetcode.cn/problems/faulty-keyboard/description/?envType=daily-question&envId=2024-04-01
    def finalString(self, s: str) -> str:
        q = deque()
        tail = True
        for c in s:
            if c == 'i':
                tail = not tail  # 修改添加方向
            elif tail:  # 加尾部
                q.append(c)
            else:  # 加头部
                q.appendleft(c)
        return ''.join(q if tail else reversed(q))

    # 20240402
    # https://leetcode.cn/problems/all-possible-full-binary-trees/description/?envType=daily-question&envId=2024-04-02
    def allPossibleFBT(self, N):
        """
        :type N: int
        :rtype: List[TreeNode]
        """
        if N%2==0:
            return []

        if N==1:
            return [TreeNode(0)]

        def createTrees(N):
            if N==0:
                return []
            if N==1:
                return [TreeNode(0)]
            num = N-1
            res = []
            for i in range(1,num+1):
                if i%2!=0:
                    lefts = createTrees(i)
                    rights = createTrees(num-i)
                    for left in lefts:
                        for right in rights:
                            cur =TreeNode(0)
                            cur.left = left
                            cur.right = right
                            res.append(cur)
            return res

        return createTrees(N)

    # 20240403
    # https://leetcode.cn/problems/find-a-corresponding-node-of-a-binary-tree-in-a-clone-of-that-tree/description/?envType=daily-question&envId=2024-04-03
    def getTargetCopy(self, original: TreeNode, cloned: TreeNode, target: TreeNode) -> TreeNode:
        if original is None or original is target:
            return cloned
        return self.getTargetCopy(original.left, cloned.left, target) or \
               self.getTargetCopy(original.right, cloned.right, target)

    # 20240404
    # https://leetcode.cn/problems/all-ancestors-of-a-node-in-a-directed-acyclic-graph/?envType=daily-question&envId=2024-04-04
    def getAncestors(self, n: int, edges: List[List[int]]) -> List[List[int]]:
        g = [[] for _ in range(n)]
        for x, y in edges:
            g[y].append(x)  # 反向建图

        def dfs(x: int) -> None:
            vis[x] = True  # 避免重复访问
            for y in g[x]:
                if not vis[y]:
                    dfs(y)  # 只递归没有访问过的点

        ans = [None] * n
        for i in range(n):
            vis = [False] * n
            dfs(i)  # 从 i 开始 DFS
            vis[i] = False  # ans[i] 不含 i
            ans[i] = [j for j, b in enumerate(vis) if b]
        return ans

    # 20240405
    # https://leetcode.cn/problems/maximum-difference-between-node-and-ancestor/description/?envType=daily-question&envId=2024-04-05
    def maxAncestorDiff(self, root: Optional[TreeNode]) -> int:
        ans = 0
        def dfs(node: Optional[TreeNode], mn: int, mx: int) -> None:
            if node is None:
                nonlocal ans
                ans = max(ans, mx - mn)
                return
            mn = min(mn, node.val)
            mx = max(mx, node.val)
            dfs(node.left, mn, mx)
            dfs(node.right, mn, mx)
        dfs(root, root.val, root.val)
        return ans

    # 20240408
    # https://leetcode.cn/problems/minimum-number-of-operations-to-make-array-continuous/description/?envType=daily-question&envId=2024-04-08
    def minOperations(self, nums: List[int]) -> int:
        n = len(nums)
        a = sorted(set(nums))  # 去重排序
        ans = left = 0
        for i, x in enumerate(a):
            while a[left] < x - n + 1:  # a[left] 不在窗口内
                left += 1
            ans = max(ans, i - left + 1)
        return n - ans

    # 20240409
    # https://leetcode.cn/problems/maximum-count-of-positive-integer-and-negative-integer/description/?envType=daily-question&envId=2024-04-09
    def maximumCount(self, nums: List[int]) -> int:
        neg = pos = 0
        for x in nums:
            if x < 0:
                neg += 1
            elif x > 0:
                pos += 1
        return max(neg, pos)

    # 20240410
    # https://leetcode.cn/problems/maximum-binary-string-after-change/description/?envType=daily-question&envId=2024-04-10
    def maximumBinaryString(self, binary: str) -> str:
        i = binary.find('0')
        if i < 0:  # binary 全是 '1'
            return binary
        cnt1 = binary.count('1', i)  # 统计 binary[i:] 中 '1' 的个数
        return '1' * (len(binary) - 1 - cnt1) + '0' + '1' * cnt1

    # 20240411
    # https://leetcode.cn/problems/tree-of-coprimes/description/?envType=daily-question&envId=2024-04-11
    def getCoprimes(self, nums: List[int], edg: List[List[int]]) -> List[int]:
        def gcd(i,j):
            if i%j==0 or j%i==0:
                return min(i,j)
            elif i>j:
                return gcd(i%j,j)
            else:
                return gcd(i,j%i)
        dic={}
        dic[1]=set([i for i in range(1,51)])
        for i in range(2,51):
            res=[]
            for j in range(1,51):
                if gcd(i,j)==1:
                    res.append(j)
            dic[i]=set(res)
        edges={}
        for i in edg:
            a,b=i[0],i[1]
            if a not in edges:
                edges[a]=[b]
            else:
                edges[a].append(b)
            if b not in edges:
                edges[b]=[a]
            else:
                edges[b].append(a)
        ans={}
        covered={0}
        for i in range(len(nums)):
            ans[i]=-1
        def dfs(cur_node,cur):
            nonlocal ans
            ans[cur_node]=cur[nums[cur_node]]
            t=cur[:]
            for i in dic[nums[cur_node]]:
                t[i]=cur_node
            if cur_node in edges:
                for children in edges[cur_node]:
                    if children not in covered:
                        covered.add(children)
                        dfs(children,t)
        cur=[-1]*51
        dfs(0,cur)
       
        return [ans[i] for i in range(len(nums))]

    # 20240412
    # https://leetcode.cn/problems/find-champion-i/description/?envType=daily-question&envId=2024-04-12
    def findChampion(self, grid: List[List[int]]) -> int:
        for i, row in enumerate(grid):
            if sum(row) == len(grid) - 1:
                return i

    # 20240413
    # https://leetcode.cn/problems/find-champion-ii/description/?envType=daily-question&envId=2024-04-13
    def findChampion(self, n: int, edges: List[List[int]]) -> int:
        is_weak = [False] * n
        for _, y in edges:
            is_weak[y] = True  # 不是冠军

        ans = -1
        for i, weak in enumerate(is_weak):
            if weak:
                continue
            if ans != -1:
                return -1  # 冠军只能有一个
            ans = i
        return ans

    # 20240416
    # https://leetcode.cn/problems/minimize-malware-spread/description/?envType=daily-question&envId=2024-04-16
    def minMalwareSpread(self, graph: List[List[int]], initial: List[int]) -> int:
        n = len(graph)
        ids = [0] * n
        id_to_size = dict()
        idx = 0

        for i in range(n):
            if ids[i] == 0:
                idx += 1
                size = 1
                q = deque([i])
                ids[i] = idx
                while q:
                    u = q.popleft()
                    for v in range(n):
                        if ids[v] == 0 and graph[u][v] == 1:
                            size += 1
                            q.append(v)
                            ids[v] = idx
                id_to_size[idx] = size
        
        id_to_initials = defaultdict(int)
        for u in initial:
            id_to_initials[ids[u]] += 1
        
        ans, ans_removed = n + 1, 0
        for u in initial:
            removed = (id_to_size[ids[u]] if id_to_initials[ids[u]] == 1 else 0)
            if removed > ans_removed or (removed == ans_removed and u < ans):
                ans, ans_removed = u, removed
        
        return ans

    # 20240417
    # https://leetcode.cn/problems/minimize-malware-spread-ii/description/?envType=daily-question&envId=2024-04-17
    def minMalwareSpread(self, graph: List[List[int]], initial: List[int]) -> int:
        st = set(initial)
        vis = [False] * len(graph)
        def dfs(x: int) -> None:
            vis[x] = True
            nonlocal node_id, size
            size += 1
            for y, conn in enumerate(graph[x]):
                if conn == 0:
                    continue
                if y in st:
                    # 按照 924 题的状态机更新 node_id
                    # 注意避免重复统计，例如上图中的 0 有两条不同路径可以遇到 1
                    if node_id != -2 and node_id != y:
                        node_id = y if node_id == -1 else -2
                elif not vis[y]:
                    dfs(y)

        cnt = Counter()
        for i, seen in enumerate(vis):
            if seen or i in st:
                continue
            node_id = -1
            size = 0
            dfs(i)
            if node_id >= 0:  # 只找到一个在 initial 中的节点
                # 删除节点 node_id 可以让 size 个点不被感染
                cnt[node_id] += size

        # size 取反计算最大值，相同最大值取 node_id 最小值
        return min((-size, node_id) for node_id, size in cnt.items())[1] if cnt else min(initial)

    # 20240418
    # https://leetcode.cn/problems/find-original-array-from-doubled-array/description/?envType=daily-question&envId=2024-04-18
    def findOriginalArray(self, changed: List[int]) -> List[int]:
        changed.sort()
        ans = []
        q = deque()
        for x in changed:
            if q:
                if q[0] < x:  # 无法配对
                    return []
                if q[0] == x:  # 配对成功
                    q.popleft()  # 清除一个标记
                    continue
            ans.append(x)
            q.append(x * 2)  # 添加双倍标记
        # 只有所有双倍标记都被清除掉，才能说明 changed 是一个双倍数组
        return [] if q else ans

    # 20240419
    # https://leetcode.cn/problems/minimum-skips-to-arrive-at-meeting-on-time/description/?envType=daily-question&envId=2024-04-19
    def minSkips(self, dist: List[int], speed: int, hoursBefore: int) -> int:
        n = len(dist)
        if sum(dist) > hoursBefore * speed:
            return -1

        def cal(x, y):
            return x if x % y == 0 else y * (x // y + 1)

        @lru_cache(None)
        def dfs(idx, re):
            if idx == n:
                return 0
            # 不跳过休息时间, 则需 "向上取整"
            res = cal(dist[idx] + dfs(idx + 1, re), speed)
            if re:
                res = min(res, dist[idx] + dfs(idx + 1, re - 1))
            return res

        ans = -1
        for i in range(n):
            if dfs(0, i) <= hoursBefore * speed:
                ans = i
                break
        dfs.cache_clear()
        return ans
    
    # 20240420
    # https://leetcode.cn/problems/combination-sum/description/?envType=daily-question&envId=2024-04-20
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(
            state: list[int], target: int, choices: list[int], start: int, res: list[list[int]]
        ):
            """回溯算法：子集和 I"""
            # 子集和等于 target 时，记录解
            if target == 0:
                res.append(list(state))
                return
            # 遍历所有选择
            # 剪枝二：从 start 开始遍历，避免生成重复子集
            for i in range(start, len(choices)):
                # 剪枝一：若子集和超过 target ，则直接结束循环
                # 这是因为数组已排序，后边元素更大，子集和一定超过 target
                if target - choices[i] < 0:
                    break
                # 尝试：做出选择，更新 target, start
                state.append(choices[i])
                # 进行下一轮选择
                backtrack(state, target - choices[i], choices, i, res)
                # 回退：撤销选择，恢复到之前的状态
                state.pop()

        state = []  # 状态（子集）
        candidates.sort()  # 对 candidates 进行排序
        start = 0  # 遍历起始点
        res = []  # 结果列表（子集列表）
        backtrack(state, target, candidates, start, res)
        return res

    # 20240421
    # https://leetcode.cn/problems/combination-sum-iii/description/?envType=daily-question&envId=2024-04-21
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        ans = []
        path = []
        def dfs(i: int, t: int) -> None:
            d = k - len(path)  # 还要选 d 个数
            if t < 0 or t > (i * 2 - d + 1) * d // 2:  # 剪枝
                return
            if d == 0:  # 找到一个合法组合
                ans.append(path.copy())
                return
            for j in range(i, d - 1, -1):
                path.append(j)
                dfs(j - 1, t - j)
                path.pop()
        dfs(9, n)
        return ans

    # 20240422
    # https://leetcode.cn/problems/combination-sum-iv/description/?envType=daily-question&envId=2024-04-22
    def combinationSum4(self, nums: List[int], target: int) -> int:
        f = [1] + [0] * target
        for i in range(1, target + 1):
            f[i] = sum(f[i - x] for x in nums if x <= i)
        return f[target]
    
    # 20240423
    # https://leetcode.cn/problems/grumpy-bookstore-owner/description/?envType=daily-question&envId=2024-04-23
    def maxSatisfied(self, customers: List[int], grumpy: List[int], X: int) -> int:
        N = len(customers)
        sum_ = 0
        # 所有不生气时间内的顾客总数
        for i in range(N):
            if grumpy[i] == 0:
                sum_ += customers[i]
        # 生气的 X 分钟内，会让多少顾客不满意
        curValue = 0
        # 先计算起始的 [0, X) 区间
        for i in range(X):
            if grumpy[i] == 1:
                curValue += customers[i]
        resValue = curValue
        # 然后利用滑动窗口，每次向右移动一步
        for i in range(X, N):
            # 如果新进入窗口的元素是生气的，累加不满意的顾客到滑动窗口中
            if grumpy[i] == 1:
                curValue += customers[i]
            # 如果离开窗口的元素是生气的，则从滑动窗口中减去该不满意的顾客数
            if grumpy[i - X] == 1:
                curValue -= customers[i - X]
            # 求所有窗口内不满意顾客的最大值
            resValue = max(resValue, curValue)
        # 最终结果是：不生气时的顾客总数 + 窗口X内挽留的因为生气被赶走的顾客数
        return sum_ + resValue

    # 20240424
    # https://leetcode.cn/problems/amount-of-time-for-binary-tree-to-be-infected/description/?envType=daily-question&envId=2024-04-24
    def amountOfTime(self, root: Optional[TreeNode], start: int) -> int:
        fa = {}
        start_node = None
        def dfs(node: Optional[TreeNode], from_: Optional[TreeNode]) -> None:
            if node is None:
                return
            fa[node] = from_  # 记录每个节点的父节点
            if node.val == start:  # 找到 start
                nonlocal start_node
                start_node = node
            dfs(node.left, node)
            dfs(node.right, node)
        dfs(root, None)

        def maxDepth(node: Optional[TreeNode], from_: TreeNode) -> int:
            if node is None:
                return -1  # 注意这里是 -1，因为 start 的深度为 0
            return max(maxDepth(x, node) for x in (node.left, node.right, fa[node]) if x != from_) + 1
        return maxDepth(start_node, start_node)

    # 20240425
    # https://leetcode.cn/problems/total-distance-traveled/description/?envType=daily-question&envId=2024-04-25
    def distanceTraveled(self, mainTank: int, additionalTank: int) -> int:
        ans = 0
        while mainTank >= 5:
            mainTank -= 5
            ans += 50
            if additionalTank:
                additionalTank -= 1
                mainTank += 1
        return ans + mainTank * 10
    
    # 20240427
    # https://leetcode.cn/problems/find-the-width-of-columns-of-a-grid/description/?envType=daily-question&envId=2024-04-27
    def findColumnWidth(self, grid: List[List[int]]) -> List[int]:
        return [max(len(str(x)) for x in col)
                for col in zip(*grid)]

    # 20240428
    # https://leetcode.cn/problems/convert-to-base-2/description/?envType=daily-question&envId=2024-04-28
    def baseNeg2(self, n: int) -> str:
        return str(n) if n in (0, 1) else self.baseNeg2((n - (n & 1)) // -2) + str(n & 1) 

    # 20240429
    # https://leetcode.cn/problems/sort-the-matrix-diagonally/description/?envType=daily-question&envId=2024-04-29
    def diagonalSort(self, mat: List[List[int]]) -> List[List[int]]:
        m, n = len(mat), len(mat[0])
        for k in range(1 - n, m):  # k = i - j
            left_i, right_i = max(k, 0), min(k + n, m)
            a = sorted(mat[i][i - k] for i in range(left_i, right_i))
            for i in range(left_i, right_i):
                mat[i][i - k] = a[i - left_i]
        return mat

    # 20240430
    # https://leetcode.cn/problems/number-of-employees-who-met-the-target/description/?envType=daily-question&envId=2024-04-30
    def numberOfEmployeesWhoMetTarget(self, hours: List[int], target: int) -> int:
        return sum(h >= target for h in hours)

    # 20240501
    # https://leetcode.cn/problems/total-cost-to-hire-k-workers/description/?envType=daily-question&envId=2024-05-01
    def totalCost(self, costs: List[int], k: int, candidates: int) -> int:
        n = len(costs)
        if candidates * 2 + k > n:
            # 也可以 sum(nsmallest(k, costs))，但效率不如直接排序
            costs.sort()
            return sum(costs[:k])

        pre = costs[:candidates]
        suf = costs[-candidates:]
        heapify(pre)
        heapify(suf)

        ans = 0
        i = candidates
        j = n - 1 - candidates
        for _ in range(k):
            if pre[0] <= suf[0]:
                ans += heapreplace(pre, costs[i])
                i += 1
            else:
                ans += heapreplace(suf, costs[j])
                j -= 1
        return ans
    
    # 20240502
    # https://leetcode.cn/problems/minimum-cost-to-hire-k-workers/description/?envType=daily-question&envId=2024-05-02
    def mincostToHireWorkers(self, quality: List[int], wage: List[int], k: int) -> float:
        pairs = sorted(zip(quality, wage), key=lambda p: p[1] / p[0])  # 按照 r 值排序
        h = [-q for q, _ in pairs[:k]]  # 加负号变成最大堆
        heapify(h)
        sum_q = -sum(h)
        ans = sum_q * pairs[k - 1][1] / pairs[k - 1][0]  # 选 r 值最小的 k 名工人
        for q, w in pairs[k:]:  # 后面的工人 r 值更大
            if q < -h[0]:  # 但是 sum_q 可以变小，从而可能得到更优的答案
                sum_q += heapreplace(h, -q) + q  # 更新堆顶和 sum_q
                ans = min(ans, sum_q * w / q)
        return ans

    # 20240503
    # https://leetcode.cn/problems/average-salary-excluding-the-minimum-and-maximum-salary/description/?envType=daily-question&envId=2024-05-03
    def average(self, salary: List[int]) -> float:
        s = sum(salary)
        m = min(salary)
        M = max(salary)
        n = len(salary)
        return (s - m - M) / (n - 2)

    # 20240504
    # https://leetcode.cn/problems/maximum-profit-in-job-scheduling/description/?envType=daily-question&envId=2024-05-04
    def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int:
        @cache
        def dfs(i):
            if i >= n:
                return 0
            _, e, p = jobs[i]
            j = bisect_left(jobs, e, lo=i + 1, key=lambda x: x[0])
            return max(dfs(i + 1), p + dfs(j))

        jobs = sorted(zip(startTime, endTime, profit))
        n = len(profit)
        return dfs(0)

    # 20240505
    # https://leetcode.cn/problems/defuse-the-bomb/description/?envType=daily-question&envId=2024-05-05
    def decrypt(self, code: List[int], k: int) -> List[int]:
        n = len(code)
        ans = [0] * n
        if k == 0:
            return ans
        for i in range(n):
            if k > 0:
                for j in range(i + 1, i + k + 1):
                    ans[i] += code[j % n]
            else:
                for j in range(i + k, i):
                    ans[i] += code[(j + n) % n]
        return ans

    # 20240506
    # https://leetcode.cn/problems/cherry-pickup/description/?envType=daily-question&envId=2024-05-06
    def cherryPickup(self, grid: List[List[int]]) -> int:
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(t: int, j: int, k: int) -> int:
            # 不能出界，不能访问 -1 格子
            if j < 0 or k < 0 or t < j or t < k or grid[t - j][j] < 0 or grid[t - k][k] < 0:
                return -inf
            if t == 0:  # 此时 j = k = 0
                return grid[0][0]
            return max(dfs(t - 1, j, k), dfs(t - 1, j, k - 1), dfs(t - 1, j - 1, k), dfs(t - 1, j - 1, k - 1)) + \
                grid[t - j][j] + (grid[t - k][k] if k != j else 0)
        n = len(grid)
        return max(dfs(n * 2 - 2, n - 1, n - 1), 0)

    # 20240507
    # https://leetcode.cn/problems/cherry-pickup-ii/description/?envType=daily-question&envId=2024-05-07
    def cherryPickup(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, j: int, k: int) -> int:
            if i == m or j < 0 or j >= n or k < 0 or k >= n:
                return 0
            return max(dfs(i + 1, j2, k2) for j2 in (j - 1, j, j + 1) for k2 in (k - 1, k, k + 1)) + \
                   grid[i][j] + (grid[i][k] if k != j else 0)
        return dfs(0, 0, n - 1)

    # 20240508
    # https://leetcode.cn/problems/watering-plants/description/?envType=daily-question&envId=2024-05-08
    def wateringPlants(self, plants: List[int], capacity: int) -> int:
        ans = len(plants)
        water = capacity
        for i, need in enumerate(plants):
            if water < need:
                ans += i * 2
                water = capacity
            water -= need
        return ans

    # 20240509
    # https://leetcode.cn/problems/watering-plants-ii/description/?envType=daily-question&envId=2024-05-09
    def minimumRefill(self, plants: List[int], capacityA: int, capacityB: int) -> int:
        ans = 0
        a, b = capacityA, capacityB
        i, j = 0, len(plants) - 1
        while i < j:
            # Alice 给植物 i 浇水
            if a < plants[i]:
                # 没有足够的水，重新灌满水罐
                ans += 1
                a = capacityA
            a -= plants[i]
            i += 1
            # Bob 给植物 j 浇水
            if b < plants[j]:
                # 没有足够的水，重新灌满水罐
                ans += 1
                b = capacityB
            b -= plants[j]
            j -= 1
        # Alice 和 Bob 到达同一株植物，那么当前水罐中水更多的人会给这株植物浇水
        if i == j and max(a, b) < plants[i]:
            # 没有足够的水，重新灌满水罐
            ans += 1
        return ans

    # 20240510
    # https://leetcode.cn/problems/count-tested-devices-after-test-operations/description/?envType=daily-question&envId=2024-05-10
    def countTestedDevices(self, batteryPercentages: List[int]) -> int:
        dec = 0
        for x in batteryPercentages:
            if x > dec:
                dec += 1
        return dec

    # 2040511
    # https://leetcode.cn/problems/minimum-amount-of-time-to-collect-garbage/description/?envType=daily-question&envId=2024-05-11
    def garbageCollection(self, garbage: List[str], travel: List[int]) -> int:
        ans = sum(map(len, garbage)) + sum(travel) * 3
        for c in "MPG":
            for g, t in zip(reversed(garbage), reversed(travel)):
                if c in g:
                    break
                ans -= t  # 没有垃圾 c，多跑了
        return ans

    # 20240512
    # https://leetcode.cn/problems/minimum-number-of-days-to-eat-n-oranges/description/?envType=daily-question&envId=2024-05-12
    def minDays(self, n: int) -> int:
        # 记忆化搜索，返回吃完n个橘子的最少天数
        def dfs(i: int) -> int:
            if i == 0 or i == 1:
                return i   # n=0或1，直接返回n
            if i not in dp:
                # 未处理则处理
                # 如果n不是2的倍数，先将n变成2的倍数，则n%2天每天吃一个，然后n变成2的倍数，花费一天吃n/2，剩下的状态就取决于剩下的n/2
                # 如果n不是3的倍数，先将n变成3的倍数，则n%3天每天吃一个，然后n变成3的倍数，花费一天吃n/3*2，剩下的状态就取决于剩下的n/3
                dp[i] = min(dfs(i // 2) + i % 2 + 1, dfs(i // 3) + i % 3 + 1)
            return dp[i]

        dp = {} # 记忆化存储，dp[i]表示吃完i个橘子的最少天数
        return dfs(n)

    # 20240513
    # https://leetcode.cn/problems/rotting-oranges/description/?envType=daily-question&envId=2024-05-13
    def orangesRotting(self, grid: List[List[int]]) -> int:
        row = len(grid)
        col = len(grid[0])
        rotten = {(i, j) for i in range(row) for j in range(col) if grid[i][j] == 2} # 腐烂集合
        fresh = {(i, j) for i in range(row) for j in range(col) if grid[i][j] == 1}  # 新鲜集合
        time = 0
        while fresh:
            if not rotten: return -1
            rotten = {(i + di, j + dj) for i, j in rotten for di, dj in [(0, 1), (0, -1), (1, 0), (-1, 0)] if (i + di, j + dj) in fresh} # 即将腐烂的如果在新鲜的集合中，就将它腐烂
            fresh -= rotten # 剔除腐烂的
            time += 1
        return time

    # 20240514
    # https://leetcode.cn/problems/minimum-rounds-to-complete-all-tasks/description/?envType=daily-question&envId=2024-05-14
    def minimumRounds(self, tasks: List[int]) -> int:
        cnt = Counter(tasks)
        if 1 in cnt.values():
            return -1
        return sum((c + 2) // 3 for c in cnt.values())

    # 20240515
    # https://leetcode.cn/problems/minimum-time-to-complete-all-tasks/description/?envType=daily-question&envId=2024-05-15
    def findMinimumTime(self, tasks: List[List[int]]) -> int:
        tasks.sort(key=lambda t: t[1])
        run = [False] * (tasks[-1][1] + 1)
        for start, end, d in tasks:
            d -= sum(run[start: end + 1])  # 去掉运行中的时间点
            if d <= 0:  # 该任务已完成
                continue
            # 该任务尚未完成，从后往前找没有运行的时间点
            for i in range(end, start - 1, -1):
                if run[i]:  # 已运行
                    continue
                run[i] = True  # 运行
                d -= 1
                if d == 0:
                    break
        return sum(run)
    
    # 20240516
    # https://leetcode.cn/problems/maximum-number-of-weeks-for-which-you-can-work/description/?envType=daily-question&envId=2024-05-16
    def numberOfWeeks(self, milestones: List[int]) -> int:
        s = sum(milestones)
        m = max(milestones)
        return (s - m) * 2 + 1 if m > s - m + 1 else s

    # 20240517
    # https://leetcode.cn/problems/most-profit-assigning-work/description/?envType=daily-question&envId=2024-05-17
    def maxProfitAssignment(self, difficulty: List[int], profit: List[int], worker: List[int]) -> int:
        jobs = sorted(zip(difficulty, profit))
        worker.sort()
        ans = j = max_profit = 0
        for w in worker:
            while j < len(jobs) and jobs[j][0] <= w:
                max_profit = max(max_profit, jobs[j][1])
                j += 1
            ans += max_profit
        return ans

    # 20240518
    # https://leetcode.cn/problems/find-the-maximum-divisibility-score/description/?envType=daily-question&envId=2024-05-18
    def maxDivScore(self, nums: List[int], divisors: List[int]) -> int:
        max_cnt, ans = -1, 0
        for d in divisors:
            cnt = sum(1 for x in nums if x % d == 0)
            if cnt > max_cnt or cnt == max_cnt and d < ans:
                max_cnt, ans = cnt, d
        return ans

    # 20240519
    # https://leetcode.cn/problems/find-the-winner-of-an-array-game/description/?envType=daily-question&envId=2024-05-19
    def getWinner(self, arr: List[int], k: int) -> int:
        mx = arr[0]
        win = -1  # 对于 arr[0] 来说，需要连续 k+1 个回合都是最大值
        for x in arr:
            if x > mx:  # 新的最大值
                mx = x
                win = 0
            win += 1  # 获胜回合 +1
            if win == k:
                break
        return mx

    # 20240520
    # https://leetcode.cn/problems/find-longest-awesome-substring/description/?envType=daily-question&envId=2024-05-20
    def longestAwesome(self, s: str) -> int:
        D = 10  # s 中的字符种类数
        n = len(s)
        pos = [n] * (1 << D)  # n 表示没有找到异或前缀和
        pos[0] = -1  # pre[-1] = 0
        ans = pre = 0
        for i, x in enumerate(map(int, s)):
            pre ^= 1 << x
            ans = max(ans, i - pos[pre],  # 偶数
                      max(i - pos[pre ^ (1 << d)] for d in range(D)))  # 奇数
            if pos[pre] == n:  # 首次遇到值为 pre 的前缀异或和，记录其下标 i
                pos[pre] = i
        return ans

    # 20240521
    # https://leetcode.cn/problems/find-the-maximum-achievable-number/description/?envType=daily-question&envId=2024-05-21
    def theMaximumAchievableX(self, num: int, t: int) -> int:
        return num + t * 2

    # 20240522
    # https://leetcode.cn/problems/find-players-with-zero-or-one-losses/description/?envType=daily-question&envId=2024-05-22
    def findWinners(self, matches: List[List[int]]) -> List[List[int]]:
        players = set(x for m in matches for x in m)
        loss_count = Counter(loser for _, loser in matches)
        return [sorted(x for x in players if x not in loss_count),
                sorted(x for x, c in loss_count.items() if c == 1)]

    # 20240523
    # https://leetcode.cn/problems/find-the-longest-equal-subarray/description/?envType=daily-question&envId=2024-05-23
    def longestEqualSubarray(self, nums: List[int], k: int) -> int:
        pos_lists = defaultdict(list)
        for i, x in enumerate(nums):
            pos_lists[x].append(i - len(pos_lists[x]))

        ans = 0
        for pos in pos_lists.values():
            if len(pos) <= ans:
                continue  # 无法让 ans 变得更大
            left = 0
            for right, p in enumerate(pos):
                while p - pos[left] > k:  # 要删除的数太多了
                    left += 1
                ans = max(ans, right - left + 1)
        return ans

    # 20240524
    # https://leetcode.cn/problems/find-the-most-competitive-subsequence/description/?envType=daily-question&envId=2024-05-24
    def mostCompetitive(self, nums: List[int], k: int) -> List[int]:
        st = []
        for i, x in enumerate(nums):
            while st and x < st[-1] and len(st) + len(nums) - i > k:
                st.pop()
            if len(st) < k:
                st.append(x)
        return st

    # 20240525
    # https://leetcode.cn/problems/find-indices-with-index-and-value-difference-i/description/?envType=daily-question&envId=2024-05-25
    def findIndices(self, nums: List[int], indexDifference: int, valueDifference: int) -> List[int]:
        max_idx = min_idx = 0
        for j in range(indexDifference, len(nums)):
            i = j - indexDifference
            if nums[i] > nums[max_idx]:
                max_idx = i
            elif nums[i] < nums[min_idx]:
                min_idx = i
            if nums[max_idx] - nums[j] >= valueDifference:
                return [max_idx, j]
            if nums[j] - nums[min_idx] >= valueDifference:
                return [min_idx, j]
        return [-1, -1]

    # 20240526
    # https://leetcode.cn/problems/find-kth-largest-xor-coordinate-value/description/?envType=daily-question&envId=2024-05-26
    def kthLargestValue(self, matrix: List[List[int]], k: int) -> int:
        m, n = len(matrix), len(matrix[0])
        s = [[0] * (n + 1) for _ in range(m + 1)]
        for i, row in enumerate(matrix):
            for j, x in enumerate(row):
                s[i + 1][j + 1] = s[i + 1][j] ^ s[i][j + 1] ^ s[i][j] ^ x
        return sorted(x for row in s[1:] for x in row[1:])[-k]

    # 20240527
    # https://leetcode.cn/problems/find-missing-observations/description/?envType=daily-question&envId=2024-05-27
    def missingRolls(self, rolls: List[int], mean: int, n: int) -> List[int]:
        rem = mean * (n + len(rolls)) - sum(rolls)
        if not n <= rem <= n * 6:
            return []
        avg, extra = divmod(rem, n)
        return [avg + 1] * extra + [avg] * (n - extra)

    # 20240528
    # https://leetcode.cn/problems/find-the-peaks/description/?envType=daily-question&envId=2024-05-28
    def findPeaks(self, mountain: List[int]) -> List[int]:
        return [i for i in range(1, len(mountain) - 1)
                if mountain[i - 1] < mountain[i] > mountain[i + 1]]

    # 20240529
    # https://leetcode.cn/problems/find-longest-special-substring-that-occurs-thrice-i/description/?envType=daily-question&envId=2024-05-29
    def maximumLength(self, s: str) -> int:
        groups = defaultdict(list)
        cnt = 0
        for i, ch in enumerate(s):
            cnt += 1
            if i + 1 == len(s) or ch != s[i + 1]:
                groups[ch].append(cnt)  # 统计连续字符长度
                cnt = 0

        ans = 0
        for a in groups.values():
            a.sort(reverse=True)
            a.extend([0, 0])  # 假设还有两个空串
            ans = max(ans, a[0] - 2, min(a[0] - 1, a[1]), a[2])

        return ans if ans else -1

    # 20240530
    # https://leetcode.cn/problems/find-longest-special-substring-that-occurs-thrice-ii/description/?envType=daily-question&envId=2024-05-30
    def maximumLength(self, s: str) -> int:
        groups = defaultdict(list)
        cnt = 0
        for i, ch in enumerate(s):
            cnt += 1
            if i + 1 == len(s) or ch != s[i + 1]:
                groups[ch].append(cnt)  # 统计连续字符长度
                cnt = 0

        ans = 0
        for a in groups.values():
            a.sort(reverse=True)
            a.extend([0, 0])  # 假设还有两个空串
            ans = max(ans, a[0] - 2, min(a[0] - 1, a[1]), a[2])

        return ans if ans else -1

    # 20240531
    # https://leetcode.cn/problems/find-missing-and-repeated-values/description/?envType=daily-question&envId=2024-05-31
    def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]:
        n = len(grid)
        cnt = [0] * (n * n + 1)
        for row in grid:
            for x in row:
                cnt[x] += 1

        ans = [0, 0]
        for i in range(1, n * n + 1):
            if cnt[i] == 2:
                ans[0] = i  # 出现两次的数
            elif cnt[i] == 0:
                ans[1] = i  # 出现零次的数
        return ans

    # 20240601
    # https://leetcode.cn/problems/distribute-candies-among-children-i/description/?envType=daily-question&envId=2024-06-01
    def distributeCandies(self, n: int, limit: int) -> int:
        if limit * 3 < n:
            return 0
        res = 0
        for i in range(min(n, limit) + 1):
            for j in range(min(n - i + 1, limit) + 1):
                    res += 1 if (n - i - j >= 0 and n - i - j <= limit) else 0

        return res

    # 20240602
    # https://leetcode.cn/problems/distribute-candies/description/?envType=daily-question&envId=2024-06-02
    def distributeCandies(self, candyType: List[int]) -> int:
        return min(len(set(candyType)), len(candyType) // 2)

    # 20240603
    # https://leetcode.cn/problems/distribute-candies-to-people/description/?envType=daily-question&envId=2024-06-03
    def distributeCandies(self, candies: int, num_people: int) -> List[int]:
        ans = [0] * num_people
        i = 0
        while candies != 0:
            ans[i % num_people] += min(i + 1, candies)
            candies -= min(i + 1, candies)
            i += 1
        return ans

    # 20240604
    # https://leetcode.cn/problems/count-pairs-of-connectable-servers-in-a-weighted-tree-network/description/?envType=daily-question&envId=2024-06-04
    def countPairsOfConnectableServers(self, edges: List[List[int]], signalSpeed: int) -> List[int]:
        n = len(edges) + 1
        g = [[] for _ in range(n)]
        for x, y, wt in edges:
            g[x].append((y, wt))
            g[y].append((x, wt))

        def dfs(x: int, fa: int, s: int) -> int:
            cnt = 0 if s % signalSpeed else 1
            for y, wt in g[x]:
                if y != fa:
                    cnt += dfs(y, x, s + wt)
            return cnt

        ans = [0] * n
        for i, gi in enumerate(g):
            s = 0
            for y, wt in gi:
                cnt = dfs(y, i, wt)
                ans[i] += cnt * s
                s += cnt
        return ans

    # 20240605
    # https://leetcode.cn/problems/distribute-elements-into-two-arrays-ii/description/?envType=daily-question&envId=2024-06-05
    def resultArray(self, nums: List[int]) -> List[int]:
        arr1 = []
        arr11 = []
        arr2 = []
        arr22 = []
        for e in nums:
            if not arr1:
                arr1.append(e)
                arr11.append(e)
                continue
            if not arr2:
                arr2.append(e)
                arr22.append(e)
                continue
            idx1 = bisect_left(arr1, e + 1)
            idx2 = bisect_left(arr2, e + 1)
            cnt1 = len(arr1) - idx1
            cnt2 = len(arr2) - idx2
            if cnt1 > cnt2:
                arr1.insert(idx1, e)
                arr11.append(e)
                continue
            if cnt1 < cnt2:
                arr2.insert(idx2, e)
                arr22.append(e)
                continue
            if cnt1 == cnt2:
                if len(arr1) <= len(arr2):
                    arr1.insert(idx1, e)
                    arr11.append(e)
                else:
                    arr2.insert(idx2, e)
                    arr22.append(e)
        return arr11 + arr22

    # 20240606
    # https://leetcode.cn/problems/separate-black-and-white-balls/description/?envType=daily-question&envId=2024-06-06
    def minimumSteps(self, s: str) -> int:
        ans = cnt1 = 0
        for c in s:
            if c == '1':
                cnt1 += 1
            else:
                ans += cnt1
        return ans

    # 20240607
    # https://leetcode.cn/problems/maximum-number-of-operations-with-the-same-score-i/description/?envType=daily-question&envId=2024-06-07
    def maxOperations(self, nums: List[int]) -> int:
        s = nums[0] + nums[1]
        for i in range(3, len(nums), 2):
            if nums[i - 1] + nums[i] != s:
                return i // 2
        return len(nums) // 2

    # 20240608
    # https://leetcode.cn/problems/maximum-number-of-operations-with-the-same-score-ii/description/?envType=daily-question&envId=2024-06-08
    def maxOperations(self, nums: List[int]) -> int:
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, j: int, target: int) -> int:
            nonlocal done
            if done:
                return 0
            if i >= j:
                done = True
                return 0
            res = 0
            if nums[i] + nums[i + 1] == target:  # 删除前两个数
                res = max(res, dfs(i + 2, j, target) + 1)
            if nums[j - 1] + nums[j] == target:  # 删除后两个数
                res = max(res, dfs(i, j - 2, target) + 1)
            if nums[i] + nums[j] == target:  # 删除第一个和最后一个数
                res = max(res, dfs(i + 1, j - 1, target) + 1)
            return res

        done = False
        n = len(nums)
        res1 = dfs(2, n - 1, nums[0] + nums[1])  # 删除前两个数
        res2 = dfs(0, n - 3, nums[-2] + nums[-1])  # 删除后两个数
        res3 = dfs(1, n - 2, nums[0] + nums[-1])  # 删除第一个和最后一个数
        return max(res1, res2, res3) + 1  # 加上第一次操作

    # 20240609
    # https://leetcode.cn/problems/burst-balloons/description/?envType=daily-question&envId=2024-06-09
    def maxCoins(self, nums: List[int]) -> int:

        #nums首尾添加1，方便处理边界情况
        nums.insert(0,1)
        nums.insert(len(nums),1)

        store = [[0]*(len(nums)) for i in range(len(nums))]

        def range_best(i,j):
            m = 0 
            #k是(i,j)区间内最后一个被戳的气球
            for k in range(i+1,j): #k取值在(i,j)开区间中
                #以下都是开区间(i,k), (k,j)
                left = store[i][k]
                right = store[k][j]
                a = left + nums[i]*nums[k]*nums[j] + right
                if a > m:
                    m = a
            store[i][j] = m

        #对每一个区间长度进行循环
        for n in range(2,len(nums)): #区间长度 #长度从3开始，n从2开始
            #开区间长度会从3一直到len(nums)
            #因为这里取的是range，所以最后一个数字是len(nums)-1

            #对于每一个区间长度，循环区间开头的i
            for i in range(0,len(nums)-n): #i+n = len(nums)-1

                #计算这个区间的最多金币
                range_best(i,i+n)

        return store[0][len(nums)-1]

    # 20240610
    # https://leetcode.cn/problems/boats-to-save-people/description/?envType=daily-question&envId=2024-06-10
    def numRescueBoats(self, people: List[int], limit: int) -> int:
        people.sort()
        ans = 0
        i, j = 0, len(people) - 1
        while i <= j:
            if people[i] + people[j] <= limit:
                i += 1
            j -= 1
            ans += 1
        return ans

    # 20240611
    # https://leetcode.cn/problems/battleships-in-a-board/description/?envType=daily-question&envId=2024-06-11
    def countBattleships(self, board: List[List[str]]) -> int:
        ans = 0
        for i, row in enumerate(board):
            for j, c in enumerate(row):
                if c == 'X' and (j == 0 or row[j - 1] != 'X') and \
                                (i == 0 or board[i - 1][j] != 'X'):
                    ans += 1
        return ans

    # 20240612
    # https://leetcode.cn/problems/account-balance-after-rounded-purchase/description/?envType=daily-question&envId=2024-06-12
    def accountBalanceAfterPurchase(self, purchaseAmount: int) -> int:
        return 100 - (purchaseAmount + 5) // 10 * 10

    # 20240613
    # https://leetcode.cn/problems/maximum-elegance-of-a-k-length-subsequence/description/?envType=daily-question&envId=2024-06-13
    def findMaximumElegance(self, items: List[List[int]], k: int) -> int:
        items.sort(key=lambda p: -p[0])  # 把利润从大到小排序
        ans = total_profit = 0
        vis = set()
        duplicate = []  # 重复类别的利润
        for i, (profit, category) in enumerate(items):
            if i < k:
                total_profit += profit  # 累加前 k 个项目的利润
                if category not in vis:
                    vis.add(category)
                else:  # 重复类别
                    duplicate.append(profit)
            elif duplicate and category not in vis:  # 之前没有的类别
                vis.add(category)  # len(vis) 变大
                total_profit += profit - duplicate.pop()  # 用最小利润替换
            # else: 比前面的利润小，而且类别还重复了，选它只会让 total_profit 变小，len(vis) 不变，优雅度不会变大
            ans = max(ans, total_profit + len(vis) * len(vis))
        return ans

    # 20240614
    # https://leetcode.cn/problems/visit-array-positions-to-maximize-score/description/?envType=daily-question&envId=2024-06-14
    def maxScore(self, nums: List[int], x: int) -> int:
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, j: int) -> int:
            if i == len(nums):
                return 0
            if nums[i] % 2 != j:
                return dfs(i + 1, j)
            return max(dfs(i + 1, j), dfs(i + 1, j ^ 1) - x) + nums[i]
        return dfs(0, nums[0] % 2)

    # 20240615
    # https://leetcode.cn/problems/maximum-beauty-of-an-array-after-applying-operation/description/?envType=daily-question&envId=2024-06-15
    def maximumBeauty(self, nums: List[int], k: int) -> int:
        nums.sort()
        ans = left = 0
        for right, x in enumerate(nums):
            while x - nums[left] > k * 2:
                left += 1
            ans = max(ans, right - left + 1)
        return ans

    # 20240616
    # https://leetcode.cn/problems/longest-uncommon-subsequence-i/description/?envType=daily-question&envId=2024-06-16
    def findLUSlength(self, a: str, b: str) -> int:
        return -1 if a == b else max(len(a), len(b))

    # 20240617
    # https://leetcode.cn/problems/longest-uncommon-subsequence-ii/description/?envType=daily-question&envId=2024-06-17
    def findLUSlength(self, strs: List[str]) -> int:
        strs.sort(key=lambda x:-len(x))
        cnt = defaultdict(int)
        for i in strs:
            cnt[i] += 1
        
        def check(long, short): # 采用双指针判断short是否为long的子序列
            i = j = 0
            while i < len(long) and j < len(short):
                if long[i] == short[j]:
                    j += 1
                i += 1
            return j == len(short)
        
        cand = []
        for k, v in cnt.items():
            if v == 1:
                if not cand:
                    return len(k)
                else: # 判断k是否为前面所有长度更长字串的子序列
                    if all(not check(q,k) for q in cand):
                        return len(k)
            cand.append(k)
        return -1

    # 20240618
    # https://leetcode.cn/problems/apply-discount-to-prices/description/?envType=daily-question&envId=2024-06-18
    def discountPrices(self, sentence: str, discount: int) -> str:
        d = 1 - discount / 100
        a = sentence.split(' ')
        for i, w in enumerate(a):
            if w[0] == '$' and w[1:].isdigit():
                a[i] = f"${int(w[1:]) * d :.2f}"
        return ' '.join(a)

    # 20240619
    # https://leetcode.cn/problems/maximum-strictly-increasing-cells-in-a-matrix/description/?envType=daily-question&envId=2024-06-19
    def maxIncreasingCells(self, mat: List[List[int]]) -> int:
        g = defaultdict(list)
        for i, row in enumerate(mat):
            for j, x in enumerate(row):
                g[x].append((i, j))  # 相同元素放在同一组，统计位置

        row_max = [0] * len(mat)
        col_max = [0] * len(mat[0])
        for _, pos in sorted(g.items(), key=lambda p: p[0]):
            # 先把最大值算出来，再更新 row_max 和 col_max
            mx = [max(row_max[i], col_max[j]) + 1 for i, j in pos]
            for (i, j), f in zip(pos, mx):
                row_max[i] = max(row_max[i], f)  # 更新第 i 行的最大 f 值
                col_max[j] = max(col_max[j], f)  # 更新第 j 列的最大 f 值
        return max(row_max)

    # 20240620
    # https://leetcode.cn/problems/number-of-beautiful-pairs/description/?envType=daily-question&envId=2024-06-20
    def countBeautifulPairs(self, nums: List[int]) -> int:
        ans = 0
        cnt = [0] * 10
        for x in nums:
            for y, c in enumerate(cnt):
                if c and gcd(y, x % 10) == 1:
                    ans += c
            while x >= 10: 
                x //= 10
            cnt[x] += 1  # 统计最高位的出现次数
        return ans

    # 20240621
    # https://leetcode.cn/problems/6CE719/description/?envType=daily-question&envId=2024-06-21
    def temperatureTrend(self, temperatureA: List[int], temperatureB: List[int]) -> int:
        ans = same = 0
        for (a1, b1), (a2, b2) in pairwise(zip(temperatureA, temperatureB)):
            if (a1 > a2) - (a1 < a2) == (b1 > b2) - (b1 < b2):
                same += 1
                ans = max(ans, same)
            else:
                same = 0
        return ans

    # 20240622
    # https://leetcode.cn/problems/lexicographically-smallest-beautiful-string/description/?envType=daily-question&envId=2024-06-22
    def smallestBeautifulString(self, s: str, k: int) -> str:
        a = ord('a')
        k += a
        s = list(map(ord, s))
        n = len(s)
        i = n - 1  # 从最后一个字母开始
        s[i] += 1  # 先加一
        while i < n:
            if s[i] == k:  # 需要进位
                if i == 0:  # 无法进位
                    return ""
                # 进位
                s[i] = a
                i -= 1
                s[i] += 1
            elif i and s[i] == s[i - 1] or i > 1 and s[i] == s[i - 2]:
                s[i] += 1  # 如果 s[i] 和左侧的字符形成回文串，就继续增加 s[i]
            else:
                i += 1  # 反过来检查后面是否有回文串
        return ''.join(map(chr, s))

    # 20240623
    # https://leetcode.cn/problems/detect-capital/description/?envType=daily-question&envId=2024-06-23
    def detectCapitalUse(self, word: str) -> bool:
        cnt = sum(c.isupper() for c in word)
        return cnt == 0 or cnt == len(word) or cnt == 1 and word[0].isupper()

    # 20240624
    # https://leetcode.cn/problems/next-greater-element-ii/description/?envType=daily-question&envId=2024-06-24
    def nextGreaterElements(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        N = len(nums)
        res = [-1] * N
        stack = []
        for i in range(N * 2):
            while stack and nums[stack[-1]] < nums[i % N]:
                res[stack.pop()] = nums[i % N]
            stack.append(i % N)
        return res

    # 20240625
    # https://leetcode.cn/problems/find-a-good-subset-of-the-matrix/description/?envType=daily-question&envId=2024-06-25
    def goodSubsetofBinaryMatrix(self, grid: List[List[int]]) -> List[int]:
        mask_to_idx = {}
        for i, row in enumerate(grid):
            mask = 0
            for j, x in enumerate(row):
                mask |= x << j
            if mask == 0:
                return [i]
            mask_to_idx[mask] = i

        for x, i in mask_to_idx.items():
            for y, j in mask_to_idx.items():
                if (x & y) == 0:
                    return sorted((i, j))
        return []

    # 20240626
    # https://leetcode.cn/problems/special-permutations/description/?envType=daily-question&envId=2024-06-26
    def specialPerm(self, nums: List[int]) -> int:
        @cache
        def dfs(s: int, i: int) -> int:
            if s == 0:
                return 1  # 找到一个特别排列
            res = 0
            pre = nums[i]
            for j, x in enumerate(nums):
                if s >> j & 1 and (pre % x == 0 or x % pre == 0):
                    res += dfs(s ^ (1 << j), j)
            return res

        n = len(nums)
        u = (1 << n) - 1
        return sum(dfs(u ^ (1 << i), i) for i in range(n)) % 1_000_000_007

    # 20240627
    # https://leetcode.cn/problems/lexicographically-smallest-string-after-substring-operation/description/?envType=daily-question&envId=2024-06-27
    def smallestString(self, s: str) -> str:
        t = list(s)
        for i, c in enumerate(t):
            if c == 'a':
                continue
            # 继续向后遍历
            for j in range(i, len(t)):
                if t[j] == 'a':
                    break
                t[j] = chr(ord(t[j]) - 1)
            return ''.join(t)
        # 所有字母均为 a
        t[-1] = 'z'
        return ''.join(t)

    # 20240628
    # https://leetcode.cn/problems/painting-the-walls/description/?envType=daily-question&envId=2024-06-28
    def paintWalls(self, cost: List[int], time: List[int]) -> int:
        @cache  # 记忆化搜索
        def dfs(i: int, j: int) -> int:
            if j > i:  # 剩余的墙都可以免费刷
                return 0
            if i < 0:  # 上面 if 不成立，意味着 j < 0，不符合题目要求
                return inf
            return min(dfs(i - 1, j + time[i]) + cost[i], dfs(i - 1, j - 1))
        return dfs(len(cost) - 1, 0)

    # 20240629
    # https://leetcode.cn/problems/remove-trailing-zeros-from-a-string/description/?envType=daily-question&envId=2024-06-29
    def removeTrailingZeros(self, num: str) -> str:
        return num.rstrip('0')

    # 20240630
    # https://leetcode.cn/problems/target-sum/description/?envType=daily-question&envId=2024-06-30
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        s = sum(nums) - abs(target)
        if s < 0 or s % 2:
            return 0
        m = s // 2  # 背包容量

        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, c: int) -> int:
            if i < 0:
                return 1 if c == 0 else 0
            if c < nums[i]:
                return dfs(i - 1, c)  # 只能不选
            return dfs(i - 1, c) + dfs(i - 1, c - nums[i])  # 不选 + 选
        return dfs(len(nums) - 1, m)

    # 20240701
    # https://leetcode.cn/problems/maximum-path-quality-of-a-graph/description/?envType=daily-question&envId=2024-07-01
    def maximalPathQuality(self, values: List[int], edges: List[List[int]], max_time: int) -> int:
        n = len(values)
        g = [[] for _ in range(n)]
        for x, y, t in edges:
            g[x].append((y, t))
            g[y].append((x, t))

        def dfs(x: int, sum_time: int, sum_value: int) -> None:
            if x == 0:
                nonlocal ans
                ans = max(ans, sum_value)
                # 注意这里没有 return，还可以继续走
            for y, t in g[x]:
                if sum_time + t > max_time:
                    continue
                if vis[y]:
                    dfs(y, sum_time + t, sum_value)
                else:
                    vis[y] = True
                    # 每个节点的价值至多算入价值总和中一次
                    dfs(y, sum_time + t, sum_value + values[y])
                    vis[y] = False  # 恢复现场

        ans = 0
        vis = [False] * n
        vis[0] = True
        dfs(0, 0, values[0])
        return ans

    # 20240702
    # https://leetcode.cn/problems/maximum-prime-difference/description/?envType=daily-question&envId=2024-07-02
    def maximumPrimeDifference(self, nums: List[int]) -> int:
        is_prime = lambda n: n >= 2 and all(n % i for i in range(2, isqrt(n) + 1))
        i = 0
        while not is_prime(nums[i]):
            i += 1
        j = len(nums) - 1
        while not is_prime(nums[j]):
            j -= 1
        return j - i

    # 20240703
    # https://leetcode.cn/problems/maximum-prime-difference/description/?envType=daily-question&envId=2024-07-02
    def sumOfTheDigitsOfHarshadNumber(self, x: int) -> int:
        s = 0
        v = x
        while v:
            v, d = divmod(v, 10)
            s += d
        return -1 if x % s else s

    # 20240704
    # https://leetcode.cn/problems/minimum-moves-to-pick-k-ones/description/?envType=daily-question&envId=2024-07-04
    def minimumMoves(self, nums: List[int], k: int, max_changes: int) -> int:
        pos = []
        c = 0  # nums 中连续的 1 长度
        for i, x in enumerate(nums):
            if x == 0:
                continue
            pos.append(i)  # 记录 1 的位置
            c = max(c, 1)
            if i > 0 and nums[i - 1] == 1:
                if i > 1 and nums[i - 2] == 1:
                    c = 3  # 有 3 个连续的 1
                else:
                    c = max(c, 2)  # 有 2 个连续的 1

        c = min(c, k)
        if max_changes >= k - c:
            # 其余 k-c 个 1 可以全部用两次操作得到
            return max(c - 1, 0) + (k - c) * 2

        n = len(pos)
        pre_sum = list(accumulate(pos, initial=0))

        ans = inf
        # 除了 max_changes 个数可以用两次操作得到，其余的 1 只能一步步移动到 pos[i]
        size = k - max_changes
        for right in range(size, n + 1):
            # s1+s2 是 j 在 [left, right) 中的所有 pos[j] 到 pos[(left+right)/2] 的距离之和
            left = right - size
            i = left + size // 2
            s1 = pos[i] * (i - left) - (pre_sum[i] - pre_sum[left])
            s2 = pre_sum[right] - pre_sum[i] - pos[i] * (right - i)
            ans = min(ans, s1 + s2)
        return ans + max_changes * 2

    # 20240705
    # https://leetcode.cn/problems/modify-the-matrix/description/?envType=daily-question&envId=2024-07-05
    def modifiedMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
        for j in range(len(matrix[0])):
            mx = max(row[j] for row in matrix)
            for row in matrix:
                if row[j] == -1:
                    row[j] = mx
        return matrix

    # 20240706
    # https://leetcode.cn/problems/count-alternating-subarrays/description/?envType=daily-question&envId=2024-07-06
    def countAlternatingSubarrays(self, nums: List[int]) -> int:
        ans = cnt = 0
        for i in range(len(nums)):
            if i > 0 and nums[i] != nums[i - 1]:
                cnt += 1
            else:
                cnt = 1
            ans += cnt  # 有 cnt 个右端点下标为 i 的交替子数组
        return ans

    # 20240707
    # https://leetcode.cn/problems/check-if-move-is-legal/description/?envType=daily-question&envId=2024-07-07
    def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool:
        m, n = len(board), len(board[0])
        for dx, dy in (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1):
            x, y = rMove + dx, cMove + dy
            if x < 0 or x >= m or y < 0 or y >= n or board[x][y] == '.' or board[x][y] == color:
                continue
            while True:
                x += dx
                y += dy
                if x < 0 or x >= m or y < 0 or y >= n or board[x][y] == '.':
                    break
                if board[x][y] == color:
                    return True
        return False

    # 20240708
    # https://leetcode.cn/problems/find-pivot-index/description/?envType=daily-question&envId=2024-07-08
    def pivotIndex(self, nums: List[int]) -> int:
        sum_left, sum_right = 0, sum(nums)
        for i in range(len(nums)):
            sum_right -= nums[i]
            # 若左侧元素和等于右侧元素和，返回中心下标 i
            if sum_left == sum_right:
                return i
            sum_left += nums[i]
        return -1

    # 20240709
    # https://leetcode.cn/problems/minimize-manhattan-distances/description/?envType=daily-question&envId=2024-07-09
    def minimumDistance(self, points: List[List[int]]) -> int:
        max_x1, max_x2 = nlargest(2, (x + y for x, y in points))   # x 最大次大
        min_x1, min_x2 = nsmallest(2, (x + y for x, y in points))  # x 最小次小
        max_y1, max_y2 = nlargest(2, (y - x for x, y in points))   # y 最大次大
        min_y1, min_y2 = nsmallest(2, (y - x for x, y in points))  # y 最小次小

        ans = inf
        for x, y in points:
            x, y = x + y, y - x
            dx = (max_x2 if x == max_x1 else max_x1) - (min_x2 if x == min_x1 else min_x1)
            dy = (max_y2 if y == max_y1 else max_y1) - (min_y2 if y == min_y1 else min_y1)
            ans = min(ans, max(dx, dy))
        return ans

    # 20240710
    # https://leetcode.cn/problems/count-the-number-of-incremovable-subarrays-i/description/?envType=daily-question&envId=2024-07-10
    def incremovableSubarrayCount(self, a: List[int]) -> int:
        n = len(a)
        i = 0
        while i < n - 1 and a[i] < a[i + 1]:
            i += 1
        if i == n - 1:  # 每个非空子数组都可以移除
            return n * (n + 1) // 2

        ans = i + 2  # 不保留后缀的情况，一共 i+2 个
        # 枚举保留的后缀为 a[j:]
        j = n - 1
        while j == n - 1 or a[j] < a[j + 1]:
            while i >= 0 and a[i] >= a[j]:
                i -= 1
            # 可以保留前缀 a[:i+1], a[:i], ..., a[:0] 一共 i+2 个
            ans += i + 2
            j -= 1
        return ans

    # 20240711
    # https://leetcode.cn/problems/count-the-number-of-incremovable-subarrays-ii/description/?envType=daily-question&envId=2024-07-11
    def incremovableSubarrayCount(self, a: List[int]) -> int:
        n = len(a)
        i = 0
        while i < n - 1 and a[i] < a[i + 1]:
            i += 1
        if i == n - 1:  # 每个非空子数组都可以移除
            return n * (n + 1) // 2

        ans = i + 2  # 不保留后缀的情况，一共 i+2 个
        # 枚举保留的后缀为 a[j:]
        j = n - 1
        while j == n - 1 or a[j] < a[j + 1]:
            while i >= 0 and a[i] >= a[j]:
                i -= 1
            # 可以保留前缀 a[:i+1], a[:i], ..., a[:0] 一共 i+2 个
            ans += i + 2
            j -= 1
        return ans
    
    # 20240712
    # https://leetcode.cn/problems/minimum-number-game/description/?envType=daily-question&envId=2024-07-12
    def numberGame(self, nums: List[int]) -> List[int]:
        nums.sort()
        for i in range(1, len(nums), 2):
            nums[i - 1], nums[i] = nums[i], nums[i - 1]
        return nums
    
    # 20240713
    # https://leetcode.cn/problems/find-if-array-can-be-sorted/description/?envType=daily-question&envId=2024-07-13
    def canSortArray(self, nums: List[int]) -> bool:
        n = len(nums)
        i = 0
        while i < n:
            start = i
            ones = nums[i].bit_count()
            i += 1
            while i < n and nums[i].bit_count() == ones:
                i += 1
            nums[start:i] = sorted(nums[start:i])
        return all(x <= y for x, y in pairwise(nums))

    # 20240714
    # https://leetcode.cn/problems/max-increase-to-keep-city-skyline/description/?envType=daily-question&envId=2024-07-14
    def maxIncreaseKeepingSkyline(self, grid: List[List[int]]) -> int:
        rowMax = list(map(max, grid))
        colMax = list(map(max, zip(*grid)))
        return sum(min(rowMax[i], colMax[j]) - h for i, row in enumerate(grid) for j, h in enumerate(row))

    # 20240715
    # https://leetcode.cn/problems/accounts-merge/description/?envType=daily-question&envId=2024-07-15
    def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]:
        cnt = defaultdict(list)
        """
        cnt = {
            "name": [
                set1(),
                set2()，
                ...
            ]
        }
        """
        for cur in accounts:
            name, email = cur[0], set(cur[1:])
            if name not in cnt:
                cnt[name].append(email)
            else:
                for i, q in enumerate(cnt[name]):
                    if q & email:
                        cnt[name][i] |= email # 合并邮箱
                        break
                else:
                    cnt[name].append(email)
        ans = []
        for k in cnt.keys():
            for v in cnt[k]:
                ans.append([k] + sorted(list(v)))

        if len(ans) == len(accounts): # 本轮迭代中，无法聚合更多，结束迭代
            return ans
        return self.accountsMerge(ans)

    # 20240716
    # https://leetcode.cn/problems/find-common-elements-between-two-arrays/description/?envType=daily-question&envId=2024-07-16
    def findIntersectionValues(self, nums1: List[int], nums2: List[int]) -> List[int]:
        set1 = set(nums1)
        set2 = set(nums2)
        return [sum(x in set2 for x in nums1),
                sum(x in set1 for x in nums2)]

    # 20240717
    # https://leetcode.cn/problems/number-of-possible-sets-of-closing-branches/description/?envType=daily-question&envId=2024-07-17
    def numberOfSets(self, n: int, maxDistance: int, roads: List[List[int]]) -> int:
        g = [[inf] * n for _ in range(n)]
        for x, y, wt in roads:
            g[x][y] = min(g[x][y], wt)
            g[y][x] = min(g[y][x], wt)

        f = [None] * n
        def check(s: int) -> int:
            for i, row in enumerate(g):
                if s >> i & 1:  # i 在集合 s 中
                    f[i] = row.copy()

            # Floyd 算法（只考虑在 s 中的节点）
            for k in range(n):
                if (s >> k & 1) == 0:  # k 不在集合 s 中
                    continue
                for i in range(n):
                    if (s >> i & 1) == 0 or f[i][k] == inf:
                        continue
                    for j in range(n):
                        f[i][j] = min(f[i][j], f[i][k] + f[k][j])

            # 判断保留的节点之间的最短路是否均不超过 maxDistance
            for i, di in enumerate(f):
                if (s >> i & 1) == 0:  # i 不在集合 s 中
                    continue
                for j, dij in enumerate(di[:i]):
                    if s >> j & 1 and dij > maxDistance:
                        return 0
            return 1

        # 枚举子集 s，作为保留的节点，判断这些节点否满足要求
        return sum(check(s) for s in range(1 << n))

    # 20240718
    # https://leetcode.cn/problems/minimum-time-to-visit-disappearing-nodes/description/?envType=daily-question&envId=2024-07-18
    def minimumTime(self, n: int, edges: List[List[int]], disappear: List[int]) -> List[int]:
        g = [[] for _ in range(n)]  # 稀疏图用邻接表
        for x, y, wt in edges:
            g[x].append((y, wt))
            g[y].append((x, wt))

        dis = [-1] * n
        dis[0] = 0
        h = [(0, 0)]
        while h:
            dx, x = heappop(h)
            if dx > dis[x]:  # x 之前出堆过
                continue
            for y, wt in g[x]:
                new_dis = dx + wt
                if new_dis < disappear[y] and (dis[y] < 0 or new_dis < dis[y]):
                    dis[y] = new_dis  # 更新 x 的邻居的最短路
                    heappush(h, (new_dis, y))
        return dis

    # 20240719
    # https://leetcode.cn/problems/minimum-levels-to-gain-more-points/description/?envType=daily-question&envId=2024-07-19
    def minimumLevels(self, possible: List[int]) -> int:
        # s = cnt1 - cnt0 = cnt1 - (n - cnt1) = cnt1 * 2 - n
        s = sum(possible) * 2 - len(possible)
        pre = 0
        for i, x in enumerate(possible[:-1]):
            pre += 2 if x else -2
            if pre > s:
                return i + 1
        return -1

    # 20240720
    # https://leetcode.cn/problems/minimum-moves-to-spread-stones-over-grid/description/?envType=daily-question&envId=2024-07-20
    def minimumMoves(self, grid: List[List[int]]) -> int:
        from_ = []
        to = []
        for i, row in enumerate(grid):
            for j, cnt in enumerate(row):
                if cnt > 1:
                    from_.extend([(i, j)] * (cnt - 1))
                elif cnt == 0:
                    to.append((i, j))

        ans = inf
        for from2 in permutations(from_):
            total = 0
            for (x1, y1), (x2, y2) in zip(from2, to):
                total += abs(x1 - x2) + abs(y1 - y2)
            ans = min(ans, total)
        return ans

    # 20240721
    # https://leetcode.cn/problems/maximum-subarray-sum-with-one-deletion/description/?envType=daily-question&envId=2024-07-21
    def maximumSum(self, arr: List[int]) -> bool:
        dp0, dp1, res = arr[0], 0, arr[0]
        for i in range(1, len(arr)):
            dp1 = max(dp0, dp1 + arr[i])
            dp0 = max(dp0, 0) + arr[i]
            res = max(res, max(dp0, dp1))
        return res

    # 20240722
    # https://leetcode.cn/problems/detonate-the-maximum-bombs/description/?envType=daily-question&envId=2024-07-22
    def maximumDetonation(self, bombs: List[List[int]]) -> int:
        n = len(bombs)
        g = [[] for _ in range(n)]
        for i, (x, y, r) in enumerate(bombs):
            for j, (x2, y2, _) in enumerate(bombs):
                dx = x - x2
                dy = y - y2
                if dx * dx + dy * dy <= r * r:
                    g[i].append(j)  # i 可以引爆 j

        def dfs(x: int) -> int:
            vis[x] = True
            cnt = 1
            for y in g[x]:
                if not vis[y]:
                    cnt += dfs(y)
            return cnt

        ans = 0
        for i in range(n):
            vis = [False] * n
            ans = max(ans, dfs(i))
        return ans

    # 20240723
    # https://leetcode.cn/problems/find-the-sum-of-subsequence-powers/description/?envType=daily-question&envId=2024-07-23
    def sumOfPowers(self, nums: List[int], k: int) -> int:
        nums.sort()
        # 先对数组进行排序
        n = len(nums)
        mod = 10 ** 9 + 7
        
        def f(nums, lower_diff):
            # nums 取包含第一项的子序列，使得两两差至少为 lower_diff
            n = len(nums)
            dp = [[0] * k for _ in range(n)]
            dp[0][1] = 1
            for i in range(1, n):
                # 枚举前一项
                for j in range(i):
                    if nums[i] - nums[j] < lower_diff: break
                    # 两项距离不小于 lower_diff 时开始更新
                    for v in range(k - 1):
                        # 结束元素由 j 变成 i
                        # 相当于新增一个长度，v 变为 v + 1
                        dp[i][v+1] += dp[j][v]
                        dp[i][v+1] %= mod
            # 最后统计每个位置结束的数组个数
            ans = [0] * k
            for i in range(n):
                for j in range(k):
                    ans[j] += dp[i][j]
            return ans
        
        ans = 0
        for i in range(n):
            for j in range(i):
                # 枚举最小绝对差
                v = nums[i] - nums[j]
                # 第一个 vs 表示前面的元素和绝对差中小元素的差别
                # 因为这里的最小绝对差取的是最靠前的，因此前面的差不能小于 v + 1
                vs = [nums[j] - nums[idx] for idx in range(j, -1, -1)]
                dp1 = f(vs, v + 1)
                # 第二个 vs 表示前面的元素和绝对差中大元素的差别
                # 后面的差没有限制，只需要不小于 v 即可
                vs = [nums[idx] - nums[i] for idx in range(i, n)]
                dp2 = f(vs, v)
                for x in range(1, k):
                    ans += dp1[x] * dp2[k - x] * v
                    ans %= mod
        return ans

    # 20240724
    # https://leetcode.cn/problems/relocate-marbles/description/?envType=daily-question&envId=2024-07-24
    def relocateMarbles(self, nums: List[int], moveFrom: List[int], moveTo: List[int]) -> List[int]:
        st = set(nums)
        for f, t in zip(moveFrom, moveTo):
            st.remove(f)
            st.add(t)
        return sorted(st)

    # 20240725
    # https://leetcode.cn/problems/minimum-operations-to-make-a-special-number/description/?envType=daily-question&envId=2024-07-25
    def minimumOperations(self, num: str) -> int:
        n = len(num)
        found0 = found5 = False
        for i in range(n - 1, -1, -1):
            c = num[i]
            if found0 and c in "05" or found5 and c in "27":
                return n - i - 2
            if c == '0':
                found0 = True
            elif c == '5':
                found5 = True
        return n - found0

    # 20240726
    # https://leetcode.cn/problems/find-the-value-of-the-partition/description/?envType=daily-question&envId=2024-07-26
    def findValueOfPartition(self, nums: List[int]) -> int:
        nums.sort()
        return min(y - x for x, y in pairwise(nums))

    # 20240727
    # https://leetcode.cn/problems/lexicographically-smallest-string-after-operations-with-constraint/description/?envType=daily-question&envId=2024-07-27
    def getSmallestString(self, s: str, k: int) -> str:
        s = list(s)
        for i, c in enumerate(map(ord, s)):
            dis = min(c - ord('a'), ord('z') - c + 1)
            if dis > k:
                s[i] = chr(c - k)
                break
            s[i] = 'a'
            k -= dis
        return ''.join(s)

    # 20240728
    # https://leetcode.cn/problems/falling-squares/description/?envType=daily-question&envId=2024-07-28
    def fallingSquares(self, positions: List[List[int]]) -> List[int]:
        lstp, lsth, ma, ans = [0, inf], [0], 0, [0]
        for x, a in positions:
            i, j = bisect_right(lstp, x), bisect_left(lstp, x + a)
            ma = max(lsth[i - 1 : j]) + a
            lstp, lsth = lstp[: i] + [x, x + a] + lstp[j :], lsth[: i] + [ma] + lsth[j - 1 :]                
            ans.append(max(ans[-1], ma))
        return ans[1 :]

    # 20240729
    # https://leetcode.cn/problems/baseball-game/description/?envType=daily-question&envId=2024-07-29
    def calPoints(self, operations: List[str]) -> int:
        st = []
        for op in operations:
            if op == '+':
                st.append(st[-2] + st[-1])
            elif op == 'D':
                st.append(st[-1] * 2)
            elif op == 'C':
                st.pop()
            else:
                st.append(int(op))
        return sum(st)

    # 20240730
    # https://leetcode.cn/problems/double-modular-exponentiation/description/?envType=daily-question&envId=2024-07-30
    def getGoodIndices(self, variables: List[List[int]], target: int) -> List[int]:
            return [i for i, (a, b, c, m) in enumerate(variables)
                    if pow(pow(a, b, 10), c, m) == target]

    # 20240731
    # https://leetcode.cn/problems/minimum-rectangles-to-cover-points/description/?envType=daily-question&envId=2024-07-31
    def minRectanglesToCoverPoints(self, points: List[List[int]], w: int) -> int:
        points.sort(key=lambda p: p[0])
        ans = 0
        x2 = -1
        for x, _ in points:
            if x > x2:
                ans += 1
                x2 = x + w
        return ans     

    # 20240801
    # https://leetcode.cn/problems/uOAnQW/description/?envType=daily-question&envId=2024-08-01 
    def maxmiumScore(self, cards: List[int], cnt: int) -> int:
        cards.sort(reverse=True)
        s = sum(cards[:cnt])  # 最大的 cnt 个数之和
        if s % 2 == 0:  # s 是偶数
            return s

        def replace_sum(x: int) -> int:
            for v in cards[cnt:]:
                if v % 2 != x % 2:  # 找到一个最大的奇偶性和 x 不同的数
                    return s - x + v  # 用 v 替换 s
            return 0

        x = cards[cnt - 1]
        ans = replace_sum(x)  # 替换 x
        for v in cards[cnt - 1::-1]:
            if v % 2 != x % 2:  # 找到一个最小的奇偶性和 x 不同的数
                ans = max(ans, replace_sum(v))  # 替换
                break
        return ans

    # 20240802
    # https://leetcode.cn/problems/right-triangles/description/?envType=daily-question&envId=2024-08-02
    def numberOfRightTriangles(self, grid: List[List[int]]) -> int:
        col_sum = [sum(col) - 1 for col in zip(*grid)]  # 提前减一
        ans = 0
        for row in grid:
            row_sum = sum(row) - 1
            ans += row_sum * sum(cs for x, cs in zip(row, col_sum) if x)
        return ans

    # 20240803
    # https://leetcode.cn/problems/maximum-points-inside-the-square/description/?envType=daily-question&envId=2024-08-03
    def maxPointsInsideSquare(self, points: List[List[int]], s: str) -> int:
        ans = 0
        def check(size: int) -> bool:
            vis = set()
            for (x, y), c in zip(points, s):
                if abs(x) <= size and abs(y) <= size:  # 点在正方形中
                    if c in vis:
                        return True
                    vis.add(c)
            nonlocal ans
            ans = len(vis)
            return False
        # 注意 range 并不会创建 list，它是 O(1) 的
        bisect_left(range(1_000_000_001), True, key=check)
        return ans

    # 20240805
    # https://leetcode.cn/problems/non-negative-integers-without-consecutive-ones/description/?envType=daily-question&envId=2024-08-05
    def findIntegers(self, n: int) -> int:
        @cache
        def dfs(i: int, pre1: bool, is_limit: bool) -> int:
            if i < 0:
                return 1
            up = n >> i & 1 if is_limit else 1
            res = dfs(i - 1, False, is_limit and up == 0)  # 填 0
            if not pre1 and up == 1:  # 可以填 1
                res += dfs(i - 1, True, is_limit)  # 填 1
            return res
        return dfs(n.bit_length() - 1, False, True)  # 从高位到低位

    # 20240806
    # https://leetcode.cn/problems/find-all-possible-stable-binary-arrays-i/description/?envType=daily-question&envId=2024-08-06
    def numberOfStableArrays(self, zero: int, one: int, limit: int) -> int:
        @cache
        def dfs(i: int, j: int, k: int) -> int:
            if i == 0:
                return int(k == 1 and j <= limit)
            if j == 0:
                return int(k == 0 and i <= limit)
            if k == 0:
                return (
                    dfs(i - 1, j, 0)
                    + dfs(i - 1, j, 1)
                    - (0 if i - limit - 1 < 0 else dfs(i - limit - 1, j, 1))
                )
            return (
                dfs(i, j - 1, 0)
                + dfs(i, j - 1, 1)
                - (0 if j - limit - 1 < 0 else dfs(i, j - limit - 1, 0))
            )

        mod = 10**9 + 7
        ans = (dfs(zero, one, 0) + dfs(zero, one, 1)) % mod
        dfs.cache_clear()
        return ans

    # 20240807
    # https://leetcode.cn/problems/find-all-possible-stable-binary-arrays-ii/description/?envType=daily-question&envId=2024-08-07
    def numberOfStableArrays(self, zero: int, one: int, limit: int) -> int:
        mod = 10 ** 9 + 7

        @cache
        def dp(zero, one, lastBit):
            if zero == 0:
                if lastBit == 0 or one > limit:
                    return 0
                else:
                    return 1
            elif one == 0:
                if lastBit == 1 or zero > limit:
                    return 0
                else:
                    return 1
            if lastBit == 0:
                res = dp(zero - 1, one, 0) + dp(zero - 1, one, 1)
                if zero > limit:
                    res -= dp(zero - limit - 1, one, 1)
            else:
                res = dp(zero, one - 1, 0) + dp(zero, one - 1, 1)
                if one > limit:
                    res -= dp(zero, one - limit - 1, 0)
            return res % mod
            
        res = (dp(zero, one, 0) + dp(zero, one, 1)) % mod
        dp.cache_clear()
        return res

    # 20240808
    # https://leetcode.cn/problems/find-the-integer-added-to-array-i/description/?envType=daily-question&envId=2024-08-08
    def addedInteger(self, nums1: List[int], nums2: List[int]) -> int:
        return min(nums2) - min(nums1)

    # 20240809
    # https://leetcode.cn/problems/find-the-integer-added-to-array-ii/description/?envType=daily-question&envId=2024-08-09
    def minimumAddedInteger(self, nums1: List[int], nums2: List[int]) -> int:
        nums1.sort()
        nums2.sort()
        # 枚举保留 nums1[2] 或者 nums1[1] 或者 nums1[0]
        # 倒着枚举是因为 nums1[i] 越大答案越小，第一个满足的就是答案
        for i in range(2, 0, -1):
            x = nums2[0] - nums1[i]
            # 在 {nums1[i] + x} 中找子序列 nums2
            j = 0
            for v in nums1[i:]:
                if nums2[j] == v + x:
                    j += 1
                    # nums2 是 {nums1[i] + x} 的子序列
                    if j == len(nums2):
                        return x
        # 题目保证答案一定存在
        return nums2[0] - nums1[0]

    # 20240810
    # https://leetcode.cn/problems/find-building-where-alice-and-bob-can-meet/description/?envType=daily-question&envId=2024-08-10
    def leftmostBuildingQueries(self, heights: List[int], queries: List[List[int]]) -> List[int]:
        n = len(heights)
        zd = [0] * (n * 4)

        def build(l, r, rt, heights):
            if l == r:
                zd[rt] = heights[l - 1]
                return

            mid = (l + r) // 2
            build(l, mid, rt << 1, heights)
            build(mid + 1, r, rt << 1 | 1, heights)
            zd[rt] = max(zd[rt << 1], zd[rt << 1 | 1])

        def query(pos, val, l, r, rt):
            if val >= zd[rt]:
                return 0
            if l == r:
                return l
            mid = (l + r) // 2
            if pos <= mid:
                res = query(pos, val, l, mid, rt << 1)
                if res != 0:
                    return res
            return query(pos, val, mid + 1, r, rt << 1 | 1)

        build(1, n, 1, heights)
        m = len(queries)
        ans = [0] * m
        for i in range(m):
            a, b = queries[i]
            if a > b:
                a, b = b, a
            if a == b or heights[a] < heights[b]:
                ans[i] = b
                continue
            ans[i] = query(b + 1, heights[a], 1, n, 1) - 1
        return ans

    # 20240811
    # https://leetcode.cn/problems/uncrossed-lines/description/?envType=daily-question&envId=2024-08-11
    def maxUncrossedLines(self, s: List[int], t: List[int]) -> int:
        n, m = len(s), len(t)
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i, j):
            if i < 0 or j < 0:
                return 0
            if s[i] == t[j]:
                return dfs(i - 1, j - 1) + 1
            return max(dfs(i - 1, j), dfs(i, j - 1))
        return dfs(n - 1, m - 1)

    # 20240813
    # https://leetcode.cn/problems/special-array-i/description/?envType=daily-question&envId=2024-08-13
    def isArraySpecial(self, nums: List[int]) -> bool:
        return all(x % 2 != y % 2 for x, y in pairwise(nums))

    # 20240814
    # https://leetcode.cn/problems/special-array-ii/description/?envType=daily-question&envId=2024-08-14
    def isArraySpecial(self, nums: List[int], queries: List[List[int]]) -> List[bool]:
        s = list(accumulate((x % 2 == y % 2 for x, y in pairwise(nums)), initial=0))
        return [s[from_] == s[to] for from_, to in queries]

    # 20240815
    # https://leetcode.cn/problems/maximum-difference-score-in-a-grid/description/?envType=daily-question&envId=2024-08-15
    def maxScore(self, grid: List[List[int]]) -> int:
        ans = -inf
        m, n = len(grid), len(grid[0])
        f = [[inf] * (n + 1) for _ in range(m + 1)]
        for i, row in enumerate(grid):
            for j, x in enumerate(row):
                mn = min(f[i + 1][j], f[i][j + 1])
                ans = max(ans, x - mn)
                f[i + 1][j + 1] = min(mn, x)
        return ans

    # 20240816
    # https://leetcode.cn/problems/minimum-sum-of-values-by-dividing-array/description/?envType=daily-question&envId=2024-08-16
    def minimumValueSum(self, nums: List[int], andValues: List[int]) -> int:
        n, m = len(nums), len(andValues)
        @cache
        def dfs(i: int, j: int, and_: int) -> int:
            if n - i < m - j:  # 剩余元素不足
                return inf
            if j == m:  # 分了 m 段
                return 0 if i == n else inf
            and_ &= nums[i]
            res = dfs(i + 1, j, and_)  # 不划分
            if and_ == andValues[j]:  # 划分，nums[i] 是这一段的最后一个数
                res = min(res, dfs(i + 1, j + 1, -1) + nums[i])
            return res
        ans = dfs(0, 0, -1)
        return ans if ans < inf else -1

    # 20240817
    # https://leetcode.cn/problems/minimum-number-of-operations-to-make-word-k-periodic/description/?envType=daily-question&envId=2024-08-17
    def minimumOperationsToMakeKPeriodic(self, word: str, k: int) -> int:
        n = len(word)
        cnt = Counter(word[i - k: i] for i in range(k, n + 1, k))
        mx = max(cnt.values())
        return n // k - mx

    # 20240818
    # https://leetcode.cn/problems/student-attendance-record-i/description/?envType=daily-question&envId=2024-08-18
    def checkRecord(self, s: str) -> bool:
        return s.count('A') < 2 and "LLL" not in s

    # 20240819
    # https://leetcode.cn/problems/student-attendance-record-ii/description/?envType=daily-question&envId=2024-08-19
    def checkRecord(self, n: int) -> int:
        @cache
        def dfs(i, j, k):
            if i >= n:
                return 1
            ans = 0
            if j == 0:
                ans += dfs(i + 1, j + 1, 0)
            if k < 2:
                ans += dfs(i + 1, j, k + 1)
            ans += dfs(i + 1, j, 0)
            return ans % mod

        mod = 10**9 + 7
        ans = dfs(0, 0, 0)
        dfs.cache_clear()
        return ans

    # 20240820
    # https://leetcode.cn/problems/find-number-of-ways-to-reach-the-k-th-stair/description/?envType=daily-question&envId=2024-08-20
    def waysToReachStair(self, k: int) -> int:
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, j: int, pre_down: bool) -> int:
            if i > k + 1:  # 无法到达终点 k
                return 0
            res = 1 if i == k else 0
            res += dfs(i + (1 << j), j + 1, False)  # 操作二
            if i and not pre_down:
                res += dfs(i - 1, j, True)  # 操作一
            return res
        return dfs(1, 0, False)

    # 20240821
    # https://leetcode.cn/problems/maximum-number-that-sum-of-the-prices-is-less-than-or-equal-to-k/description/?envType=daily-question&envId=2024-08-21
    def findMaximumNumber(self, k: int, x: int) -> int:
        def count(num: int) -> int:
            @cache
            def dfs(i: int, cnt1: int, is_limit: bool) -> int:
                if i == 0:
                    return cnt1
                res = 0
                up = num >> (i - 1) & 1 if is_limit else 1
                for d in range(up + 1):  # 枚举要填入的数字 d
                    res += dfs(i - 1, cnt1 + (d == 1 and i % x == 0), is_limit and d == up)
                return res
            return dfs(num.bit_length(), 0, True)

        # <= k 转换成 >= k+1 的数再减一
        # 原理见 https://www.bilibili.com/video/BV1AP41137w7/
            return bisect_left(range((k + 1) << x), k + 1, key=count) - 1
    
    # 20240822
    # https://leetcode.cn/problems/minimum-array-end/description/?envType=daily-question&envId=2024-08-22
    def minEnd(self, n: int, x: int) -> int:
        n -= 1
        j = 0
        t = ~x
        while n >> j:
            lb = t & -t
            x |= (n >> j & 1) * lb
            j += 1
            t ^= lb
        return x

    # 20240823
    # https://leetcode.cn/problems/find-products-of-elements-of-big-array/description/?envType=daily-question&envId=2024-08-23
    def findProductsOfElements(self, queries: List[List[int]]) -> List[int]:
        def sum_e(k: int) -> int:
            res = n = cnt1 = sum_i = 0
            for i in range((k + 1).bit_length() - 1, -1, -1):
                c = (cnt1 << i) + (i << i >> 1)  # 新增的幂次个数
                if c <= k:
                    k -= c
                    res += (sum_i << i) + ((i * (i - 1) // 2) << i >> 1)
                    sum_i += i  # 之前填的 1 的幂次之和
                    cnt1 += 1  # 之前填的 1 的个数
                    n |= 1 << i  # 填 1
            # 剩余的 k 个幂次，由 n 的低 k 个 1 补充
            for _ in range(k):
                lb = n & -n
                res += lb.bit_length() - 1
                n ^= lb  # 去掉最低位的 1（置为 0）
            return res
        return [pow(2, sum_e(r + 1) - sum_e(l), mod) for l, r, mod in queries]

    # 20240824
    # https://leetcode.cn/problems/permutation-difference-between-two-strings/description/?envType=daily-question&envId=2024-08-24
    def findPermutationDifference(self, s: str, t: str) -> int:
        pos = {c: i for i, c in enumerate(s)}
        return sum(abs(i - pos[c]) for i, c in enumerate(t))

    # 20240825
    # https://leetcode.cn/problems/partition-to-k-equal-sum-subsets/description/?envType=daily-question&envId=2024-08-25
    def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
        def dfs(i):
            if i == len(nums):
                return True
            for j in range(k):
                if j and cur[j] == cur[j - 1]:
                    continue
                cur[j] += nums[i]
                if cur[j] <= s and dfs(i + 1):
                    return True
                cur[j] -= nums[i]
            return False

        s, mod = divmod(sum(nums), k)
        if mod:
            return False
        cur = [0] * k
        nums.sort(reverse=True)
        return dfs(0)

    # 20240826
    # https://leetcode.cn/problems/employee-importance/description/?envType=daily-question&envId=2024-08-26
    def getImportance(self, employees: List['Employee'], id: int) -> int:
        employees = {e.id: e for e in employees}
        def dfs(id: int) -> int:
            e = employees[id]
            return e.importance + sum(dfs(sub) for sub in e.subordinates)
        return dfs(id)

    # 20240827
    # https://leetcode.cn/problems/find-the-median-of-the-uniqueness-array/description/?envType=daily-question&envId=2024-08-27
    def medianOfUniquenessArray(self, nums: List[int]) -> int:
        n = len(nums)
        k = (n * (n + 1) // 2 + 1) // 2

        def check(upper: int) -> bool:
            cnt = l = 0
            freq = defaultdict(int)
            for r, in_ in enumerate(nums):
                freq[in_] += 1  # 移入右端点
                while len(freq) > upper:  # 窗口内元素过多
                    out = nums[l]
                    freq[out] -= 1  # 移出左端点
                    if freq[out] == 0:
                        del freq[out]
                    l += 1
                cnt += r - l + 1  # 右端点固定为 r 时，有 r-l+1 个合法左端点
                if cnt >= k:
                    return True
            return False

        return bisect_left(range(len(set(nums))), True, 1, key=check)

    # 20240828
    # https://leetcode.cn/problems/minimum-substring-partition-of-equal-character-frequency/description/?envType=daily-question&envId=2024-08-28
    def minimumSubstringsInPartition(self, s: str) -> int:
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int) -> int:
            if i < 0:
                return 0
            res = inf
            cnt = defaultdict(int)
            max_cnt = 0
            for j in range(i, -1, -1):
                cnt[s[j]] += 1
                max_cnt = max(max_cnt, cnt[s[j]])
                if i - j + 1 == len(cnt) * max_cnt:
                    res = min(res, dfs(j - 1) + 1)
            return res
        return dfs(len(s) - 1)

    # 20240829
    # https://leetcode.cn/problems/check-if-grid-satisfies-conditions/description/?envType=daily-question&envId=2024-08-29
    def satisfiesConditions(self, grid: List[List[int]]) -> bool:
        for i, row in enumerate(grid):
            for j, x in enumerate(row):
                if j and x == row[j - 1] or i and x != grid[i - 1][j]:
                    return False
        return True

    # 20240830
    # https://leetcode.cn/problems/sum-of-digit-differences-of-all-pairs/description/?envType=daily-question&envId=2024-08-30
    def sumDigitDifferences(self, nums: List[int]) -> int:
        ans = 0
        cnt = [[0] * 10 for _ in str(nums[0])]
        for k, x in enumerate(nums):
            i = 0
            while x:
                x, d = divmod(x, 10)
                ans += k - cnt[i][d]
                cnt[i][d] += 1
                i += 1
        return ans

    # 20240831
    # https://leetcode.cn/problems/make-a-square-with-the-same-color/description/?envType=daily-question&envId=2024-08-31
    def canMakeSquare(self, grid: List[List[str]]) -> bool:
        def check(i: int, j: int) -> bool:
            cnt = defaultdict(int)
            cnt[grid[i][j]] += 1
            cnt[grid[i][j + 1]] += 1
            cnt[grid[i + 1][j]] += 1
            cnt[grid[i + 1][j + 1]] += 1
            return cnt['B'] != 2
        return check(0, 0) or check(0, 1) or check(1, 0) or check(1, 1)

    # 20240901
    # https://leetcode.cn/problems/number-of-students-doing-homework-at-a-given-time/description/?envType=daily-question&envId=2024-09-01
    def busyStudent(self, startTime: List[int], endTime: List[int], queryTime: int) -> int:
        return sum(s <= queryTime <= e for s, e in zip(startTime, endTime))
    
    # 20240902
    # https://leetcode.cn/problems/maximize-the-confusion-of-an-exam/description/?envType=daily-question&envId=2024-09-02
    def maxConsecutiveAnswers(self, s: str, k: int) -> int:
        def get_cnt(ch):
            n, ans = len(s), 0
            i, j, cnt = 0, 0, 0
            while i < n:
                cnt += s[i] == ch
                while cnt > k:
                    cnt -= s[j] == ch
                    j += 1
                ans = max(ans, i - j + 1)
                i += 1
            return ans
        return max(get_cnt('T'), get_cnt('F'))

    # 20240903
    # https://leetcode.cn/problems/maximum-strength-of-a-group/description/?envType=daily-question&envId=2024-09-03
    def maxStrength(self, nums: List[int]) -> int:
        mn = mx = nums[0]
        for x in nums[1:]:
            mn, mx = min(mn, x, mn * x, mx * x), \
                     max(mx, x, mn * x, mx * x)
        return mx

    # 20240904
    # https://leetcode.cn/problems/happy-students/description/?envType=daily-question&envId=2024-09-04
    def countWays(self, nums: List[int]) -> int:
        nums.sort()
        ans = nums[0] > 0  # 一个学生都不选
        for i, (x, y) in enumerate(pairwise(nums), 1):
            if x < i < y:
                ans += 1
        return ans + 1  # 一定可以都选

    # 20240905
    # https://leetcode.cn/problems/clear-digits/description/?envType=daily-question&envId=2024-09-05
    def clearDigits(self, s: str) -> str:
        st = []
        for c in s:
            if c.isdigit():
                st.pop()
            else:
                st.append(c)
        return ''.join(st)

    # 20240906
    # https://leetcode.cn/problems/find-the-maximum-length-of-a-good-subsequence-i/description/?envType=daily-question&envId=2024-09-06
    def maximumLength(self, nums: List[int], k: int) -> int:
        fs = {}
        mx = [0] * (k + 1)
        for x in nums:
            if x not in fs:
                fs[x] = [0] * (k + 1)
            f = fs[x]
            for j in range(k, -1, -1):
                f[j] += 1
                if j:
                    f[j] = max(f[j], mx[j - 1] + 1)
                mx[j] = max(f[j], mx[j])
        return mx[-1]

    # 20240907
    # https://leetcode.cn/problems/find-the-maximum-length-of-a-good-subsequence-ii/description/?envType=daily-question&envId=2024-09-07
    def maximumLength(self, nums: List[int], k: int) -> int:
        fs = {}
        records = [[0] * 3 for _ in range(k + 1)]
        for x in nums:
            if x not in fs:
                fs[x] = [0] * (k + 1)
            f = fs[x]
            for j in range(k, -1, -1):
                f[j] += 1
                if j > 0:
                    mx, mx2, num = records[j - 1]
                    f[j] = max(f[j], (mx if x != num else mx2) + 1)

                # records[j] 维护 fs[.][j] 的 mx, mx2, num
                v = f[j]
                p = records[j]
                if v > p[0]:
                    if x != p[2]:
                        p[2] = x
                        p[1] = p[0]
                    p[0] = v
                elif x != p[2] and v > p[1]:
                    p[1] = v
        return records[k][0]

    # 20240908
    # https://leetcode.cn/problems/squares-of-a-sorted-array/description/?envType=daily-question&envId=2024-09-08
    def sortedSquares(self, nums: List[int]) -> List[int]:
        n = len(nums)
        ans = [0] * n
        i, j = 0, n - 1
        for p in range(n - 1, -1, -1):
            x = nums[i] * nums[i]
            y = nums[j] * nums[j]
            if x > y:  # 更大的数放右边
                ans[p] = x
                i += 1
            else:
                ans[p] = y
                j -= 1
        return ans

    # 20240909
    # https://leetcode.cn/problems/merge-nodes-in-between-zeros/description/?envType=daily-question&envId=2024-09-09
    def mergeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]:
        tail = head
        cur = head.next
        while cur.next:
            if cur.val:
                tail.val += cur.val
            else:
                tail = tail.next
                tail.val = 0
            cur = cur.next
        tail.next = None
        return head

    # 20240910
    # https://leetcode.cn/problems/count-increasing-quadruplets/description/?envType=daily-question&envId=2024-09-10
    def countQuadruplets(self, nums: List[int]) -> int:
        n = len(nums)
        great = [0] * n
        great[-1] = [0] * (n + 1)
        for k in range(n - 2, 1, -1):
            great[k] = great[k + 1].copy()  # 也可以写 great[k+1][:]
            for x in range(1, nums[k + 1]):
                great[k][x] += 1

        ans = 0
        less = [0] * (n + 1)
        for j in range(1, n - 1):
            for x in range(nums[j - 1] + 1, n + 1):
                less[x] += 1
            for k in range(j + 1, n - 1):
                if nums[j] > nums[k]:
                    ans += less[nums[k]] * great[k][nums[j]]
        return ans

    # 20240911
    # https://leetcode.cn/problems/maximize-win-from-two-segments/description/?envType=daily-question&envId=2024-09-11
    def maximizeWin(self, prizePositions: List[int], k: int) -> int:
        n = len(prizePositions)
        if k * 2 + 1 >= prizePositions[-1] - prizePositions[0]:
            return n
        ans = left = 0
        mx = [0] * (n + 1)
        for right, p in enumerate(prizePositions):
            while p - prizePositions[left] > k:
                left += 1
            ans = max(ans, mx[left] + right - left + 1)
            mx[right + 1] = max(mx[right], right - left + 1)
        return ans

    # 20240912
    # https://leetcode.cn/problems/find-the-maximum-number-of-marked-indices/description/?envType=daily-question&envId=2024-09-12
    def maxNumOfMarkedIndices(self, nums: List[int]) -> int:
        nums.sort()
        left, right = 0, len(nums) // 2 + 1  # 开区间
        while left + 1 < right:
            k = (left + right) // 2
            if all(nums[i] * 2 <= nums[i - k] for i in range(k)):
                left = k
            else:
                right = k
        return left * 2  # 最多匹配 left 对，有 left * 2 个数
    
    # 20240913
    # https://leetcode.cn/problems/maximum-number-of-robots-within-budget/description/?envType=daily-question&envId=2024-09-13
    def maximumRobots(self, chargeTimes: List[int], runningCosts: List[int], budget: int) -> int:
        ans = s = left = 0
        q = deque()
        for right, (t, c) in enumerate(zip(chargeTimes, runningCosts)):
            # 1. 入
            while q and t >= chargeTimes[q[-1]]:
                q.pop()
            q.append(right)
            s += c  # 维护 sum(runningCosts)

            # 2. 出
            while q and chargeTimes[q[0]] + (right - left + 1) * s > budget:
                if q[0] == left:
                    q.popleft()
                s -= runningCosts[left]  # 维护 sum(runningCosts)
                left += 1

            # 3. 更新答案
            ans = max(ans, right - left + 1)
        return ans

    # 20240914
    # https://leetcode.cn/problems/removing-stars-from-a-string/description/?envType=daily-question&envId=2024-09-14
    def removeStars(self, s: str) -> str:
        st = []
        for c in s:
            if c == '*':
                st.pop()
            else:
                st.append(c)
        return ''.join(st)

    # 20240915
    # https://leetcode.cn/problems/points-that-intersect-with-cars/description/?envType=daily-question&envId=2024-09-15
    def numberOfPoints(self, nums: List[List[int]]) -> int:
        m = 102
        d = [0] * m
        for start, end in nums:
            d[start] += 1
            d[end + 1] -= 1
        return sum(s > 0 for s in accumulate(d))

    # 20240916
    # https://leetcode.cn/problems/distance-between-bus-stops/description/?envType=daily-question&envId=2024-09-16
    def distanceBetweenBusStops(self, distance: List[int], s: int, t: int) -> int:
        if s > t:
            s, t = t, s
        d1 = sum(distance[s: t])
        return min(d1, sum(distance) - d1)

    # 20240917
    # https://leetcode.cn/problems/bus-routes/description/?envType=daily-question&envId=2024-09-17
    def numBusesToDestination(self, routes: List[List[int]], source: int, target: int) -> int:
        # 记录经过车站 x 的公交车编号
        stop_to_buses = defaultdict(list)
        for i, route in enumerate(routes):
            for x in route:
                stop_to_buses[x].append(i)

        # 小优化：如果没有公交车经过起点或终点，直接返回
        if source not in stop_to_buses or target not in stop_to_buses:
            # 注意原地 TP 的情况
            return -1 if source != target else 0

        # BFS
        dis = {source: 0}
        q = deque([source])
        while q:
            x = q.popleft()  # 当前在车站 x
            dis_x = dis[x]
            for i in stop_to_buses[x]:  # 遍历所有经过车站 x 的公交车 i
                if routes[i]:
                    for y in routes[i]:  # 遍历公交车 i 的路线
                        if y not in dis:  # 没有访问过车站 y
                            dis[y] = dis_x + 1  # 从 x 站上车然后在 y 站下车
                            q.append(y)
                    routes[i] = None  # 标记 routes[i] 遍历过

        return dis.get(target, -1)

    # 20240918
    # https://leetcode.cn/problems/the-latest-time-to-catch-a-bus/description/?envType=daily-question&envId=2024-09-18
    def latestTimeCatchTheBus(self, buses: List[int], passengers: List[int], capacity: int) -> int:
        buses.sort()
        passengers.sort()

        # 模拟乘客上车
        j = 0
        for t in buses:
            c = capacity
            while c and j < len(passengers) and passengers[j] <= t:
                j += 1
                c -= 1

        # 寻找插队时机
        j -= 1
        ans = buses[-1] if c else passengers[j]
        while j >= 0 and ans == passengers[j]:  # 往前找没人到达的时刻
            ans -= 1
            j -= 1
        return ans

    # 20240919
    # https://leetcode.cn/problems/length-of-the-longest-alphabetical-continuous-substring/description/?envType=daily-question&envId=2024-09-19
    def longestContinuousSubstring(self, s: str) -> int:
        ans = cnt = 1
        for x, y in pairwise(map(ord, s)):
            cnt = cnt + 1 if x + 1 == y else 1
            ans = max(ans, cnt)
        return ans

    # 20240920
    # https://leetcode.cn/problems/count-special-integers/description/?envType=daily-question&envId=2024-09-20
    def countSpecialNumbers(self, n: int) -> int:
        s = str(n)
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, mask: int, is_limit: bool, is_num: bool) -> int:
            if i == len(s):
                return 1 if is_num else 0  # is_num 为 True 表示得到了一个合法数字
            res = 0
            if not is_num:  # 可以跳过当前数位
                res = dfs(i + 1, mask, False, False)
            # 如果前面没有填数字，则必须从 1 开始（因为不能有前导零）
            low = 0 if is_num else 1
            # 如果前面填的数字都和 n 的一样，那么这一位至多填 s[i]（否则就超过 n 啦）
            up = int(s[i]) if is_limit else 9
            for d in range(low, up + 1):  # 枚举要填入的数字 d
                if mask >> d & 1 == 0:  # d 不在 mask 中，说明之前没有填过 d
                    res += dfs(i + 1, mask | (1 << d), is_limit and d == up, True)
            return res
        return dfs(0, 0, True, False)

    # 20240921
    # https://leetcode.cn/problems/node-with-highest-edge-score/description/?envType=daily-question&envId=2024-09-21
    def edgeScore(self, edges: List[int]) -> int:
        ans = 0
        score = [0] * len(edges)
        for i, to in enumerate(edges):
            score[to] += i
            if score[to] > score[ans] or score[to] == score[ans] and to < ans:
                ans = to
        return ans

    # 20240922
    # https://leetcode.cn/problems/find-the-town-judge/description/?envType=daily-question&envId=2024-09-22
    def findJudge(self, n: int, trust: List[List[int]]) -> int:
        # 出度数组，因为点数分布在 1~n, 所以此处多存储一个顶点 0，方便处理。对于下面的入度数组同理。  
        out_degrees = [0] * (n + 1)
        # 入度数组 
        in_degrees = [0] * (n + 1) 

        # 遍历边集 trust
        for u, v in trust:
            out_degrees[u] += 1 # 起点 u 出度 + 1
            in_degrees[v] += 1 # 终点 v 入度 + 1

        # 忽略顶点 0，寻找出度为 0，入度为 n-1 的顶点。
        for v in range(1, n + 1):
            if out_degrees[v] == 0 and in_degrees[v] == n - 1:
                return v

        # 小镇法官不存在
        return -1

    # 20240923
    # https://leetcode.cn/problems/best-sightseeing-pair/description/?envType=daily-question&envId=2024-09-23
    def maxScoreSightseeingPair(self, values: List[int]) -> int:
        ans = mx = 0  # mx 表示 j 左边的 values[i] + i 的最大值
        for j, v in enumerate(values):
            ans = max(ans, mx + v - j)
            mx = max(mx, v + j)
        return ans

    # 20240924
    # https://leetcode.cn/problems/maximize-number-of-subsequences-in-a-string/description/?envType=daily-question&envId=2024-09-24
    def maximumSubsequenceCount(self, text: str, pattern: str) -> int:
        x, y = pattern
        ans = cnt_x = cnt_y = 0
        for c in text:
            if c == y:
                ans += cnt_x
                cnt_y += 1
            if c == x:
                cnt_x += 1
        return ans + max(cnt_x, cnt_y)

    # 20240925
    # https://leetcode.cn/problems/naming-a-company/description/?envType=daily-question&envId=2024-09-25
    def distinctNames(self, ideas: List[str]) -> int:
        size = [0] * 26  # 集合大小
        intersection = [[0] * 26 for _ in range(26)]  # 交集大小
        groups = defaultdict(list)  # 后缀 -> 首字母列表
        for s in ideas:
            b = ord(s[0]) - ord('a')
            size[b] += 1  # 增加集合大小
            g = groups[s[1:]]
            for a in g:  # a 是和 s 有着相同后缀的字符串的首字母
                intersection[a][b] += 1  # 增加交集大小
                intersection[b][a] += 1
            g.append(b)

        ans = 0
        for a in range(1, 26):  # 枚举所有组对
            for b in range(a):
                m = intersection[a][b]
                ans += (size[a] - m) * (size[b] - m)
        return ans * 2  # 乘 2 放到最后

    # 20240926
    # https://leetcode.cn/problems/difference-between-element-sum-and-digit-sum-of-an-array/description/?envType=daily-question&envId=2024-09-26
    def differenceOfSum(self, nums: List[int]) -> int:
        ans = 0
        for x in nums:
            ans += x  # 累加元素和
            while x:
                ans -= x % 10  # 减去数位和
                x //= 10
        return ans

    # 20240927
    # https://leetcode.cn/problems/take-k-of-each-character-from-left-and-right/description/?envType=daily-question&envId=2024-09-27
    def takeCharacters(self, s: str, k: int) -> int:
        cnt = Counter(s)  # 一开始，把所有字母都取走
        if any(cnt[c] < k for c in "abc"):
            return -1  # 字母个数不足 k

        mx = left = 0
        for right, c in enumerate(s):
            cnt[c] -= 1  # 移入窗口，相当于不取走 c
            while cnt[c] < k:  # 窗口之外的 c 不足 k
                cnt[s[left]] += 1  # 移出窗口，相当于取走 s[left]
                left += 1
            mx = max(mx, right - left + 1)
        return len(s) - mx

    # 20240929
    # https://leetcode.cn/problems/time-needed-to-buy-tickets/description/?envType=daily-question&envId=2024-09-29
    def timeRequiredToBuy(self, tickets: List[int], k: int) -> int:
        tk = tickets[k]
        return sum(min(t, tk - (i > k)) for i, t in enumerate(tickets))

    # 20241001
    # https://leetcode.cn/problems/minimum-cost-for-tickets/description/?envType=daily-question&envId=2024-10-01
    def mincostTickets(self, days: List[int], costs: List[int]) -> int:
        last_day = days[-1]
        days = set(days)
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int) -> int:
            if i <= 0:
                return 0
            if i not in days:
                return dfs(i - 1)
            return min(dfs(i - 1) + costs[0], dfs(i - 7) + costs[1], dfs(i - 30) + costs[2])
        return dfs(last_day)

    # 20241002
    # https://leetcode.cn/problems/minimum-speed-to-arrive-on-time/description/?envType=daily-question&envId=2024-10-02
    def minSpeedOnTime(self, dist: List[int], hour: float) -> int:
        n = len(dist)
        h100 = round(hour * 100)  # 下面不会用到任何浮点数
        delta = h100 - (n - 1) * 100
        if delta <= 0:  # 无法到达终点
            return -1

        max_dist = max(dist)
        if h100 <= n * 100:  # 特判
            # 见题解中的公式
            return max(max_dist, (dist[-1] * 100 - 1) // delta + 1)

        def check(v: int) -> bool:
            t = n - 1  # n-1 个上取整中的 +1 先提出来
            for d in dist[:-1]:
                t += (d - 1) // v
            return (t * v + dist[-1]) * 100 <= h100 * v

        left = 0
        h = h100 // (n * 100)
        right = (max_dist - 1) // h + 1
        while left + 1 < right:
            mid = (left + right) // 2
            if check(mid):
                right = mid
            else:
                left = mid
        return right

    # 20241003
    # https://leetcode.cn/problems/minimum-cost-to-reach-destination-in-time/description/?envType=daily-question&envId=2024-10-03
    def minCost(self, maxTime: int, edges: List[List[int]], passingFees: List[int]) -> int:
        n = len(passingFees)
        g = [[] for _ in range(n)]
        for x, y, t in edges:  # 建图
            g[x].append((y, t))
            g[y].append((x, t))
        
        time_cost = [inf] * n  # 抵达每个节点的最短用时
        time_cost[0] = 0
        st = [(passingFees[0], 0, 0)]  # 按照费用来建的最小堆
        while st:
            cost, pos, time = heappop(st)
            if pos == n-1:  # 第一次找到n-1的方案就是需要的方案
                return cost
            for x, t in g[pos]:
                new_time = time + t
                if new_time > maxTime or new_time >= time_cost[x]:
                    # 超时方案舍弃
                    # 当new_time >= time_cost[x]时，说明time_cost[x]已经有了值，而不是inf
                    # 根据我们的遍历策略，在我第二次及以后访问x时，我当前的花费一定比第一次访问x要高
                    # 此时如果花费的时间还比之前更长了，那一定是个无效的方案，舍弃
                    continue
                time_cost[x] = new_time  # 更新抵达x的最短时间
                heappush(st, (cost + passingFees[x], x, new_time))
        return -1  # 到不了n-1

    # 20241004
    # https://leetcode.cn/problems/airplane-seat-assignment-probability/description/?envType=daily-question&envId=2024-10-04
    def nthPersonGetsNthSeat(self, n: int) -> float:
            return 1 if n == 1 else 0.5

    # 20241005
    # https://leetcode.cn/problems/minimum-time-to-complete-trips/description/?envType=daily-question&envId=2024-10-05
    def minimumTime(self, time: List[int], totalTrips: int) -> int:
        min_t = min(time)
        avg = (totalTrips - 1) // len(time) + 1
        left = min_t * avg - 1  # 循环不变量：sum >= totalTrips 恒为 False
        right = min(max(time) * avg, min_t * totalTrips)  # 循环不变量：sum >= totalTrips 恒为 True
        while left + 1 < right:  # 开区间 (left, right) 不为空
            mid = (left + right) // 2
            if sum(mid // t for t in time) >= totalTrips:
                right = mid  # 缩小二分区间为 (left, mid)
            else:
                left = mid  # 缩小二分区间为 (mid, right)
        # 此时 left 等于 right-1
        # sum(left) < totalTrips 且 sum(right) >= totalTrips，所以答案是 right
        return right

    # 20241006
    # https://leetcode.cn/problems/gas-station/description/?envType=daily-question&envId=2024-10-06
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        ans = min_s = s = 0  # s 表示油量，min_s 表示最小油量
        for i, (g, c) in enumerate(zip(gas, cost)):
            s += g - c  # 在 i 处加油，然后从 i 到 i+1
            if s < min_s:
                min_s = s  # 更新最小油量
                ans = i + 1  # 注意 s 减去 c 之后，汽车在 i+1 而不是 i
        # 循环结束后，s 即为 gas 之和减去 cost 之和
        return -1 if s < 0 else ans

    # 20241007
    # https://leetcode.cn/problems/minimum-number-of-refueling-stops/description/?envType=daily-question&envId=2024-10-07
    def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:
        stations.append((target, 0))
        ans, miles = 0, startFuel
        fuel_heap = []  # 下面把堆中元素取反，当作最大堆用
        for position, fuel in stations:
            while fuel_heap and miles < position:  # 没有足够的油到达 position
                miles -= heappop(fuel_heap)  # 选油量最多的油桶
                ans += 1
            if miles < position:  # 无法到达
                return -1
            heappush(fuel_heap, -fuel)  # 留着后面加油
        return ans

    # 20241008
    # https://leetcode.cn/problems/destination-city/description/?envType=daily-question&envId=2024-10-08
    def destCity(self, paths: List[List[str]]) -> str:
        set_a = set(p[0] for p in paths)
        return next(p[1] for p in paths if p[1] not in set_a)

    # 20241009
    # https://leetcode.cn/problems/find-subarray-with-bitwise-or-closest-to-k/description/?envType=daily-question&envId=2024-10-09
    def minimumDifference(self, nums: List[int], k: int) -> int:
        ans = inf
        for i, x in enumerate(nums):
            ans = min(ans, abs(x - k))
            j = i - 1
            # 如果 x 是 nums[j] 的子集，就退出循环
            while j >= 0 and nums[j] | x != nums[j]:
                nums[j] |= x
                ans = min(ans, abs(nums[j] - k))
                j -= 1
        return ans

    # 20241010
    # https://leetcode.cn/problems/find-the-number-of-good-pairs-i/description/?envType=daily-question&envId=2024-10-10
    def numberOfPairs(self, nums1: List[int], nums2: List[int], k: int) -> int:
        res = 0
        for a in nums1:
            for b in nums2:
                if a % (b * k) == 0:
                    res += 1
        return res

    # 20241011
    # https://leetcode.cn/problems/find-the-number-of-good-pairs-ii/description/?envType=daily-question&envId=2024-10-11
    def numberOfPairs(self, nums1: List[int], nums2: List[int], k: int) -> int:
        cnt = defaultdict(int)
        for x in nums1:
            if x % k:
                continue
            x //= k
            for d in range(1, isqrt(x) + 1):  # 枚举因子
                if x % d:
                    continue
                cnt[d] += 1  # 统计因子
                if d * d < x:
                    cnt[x // d] += 1  # 因子总是成对出现
        return sum(cnt[x] for x in nums2)

    # 20241012
    # https://leetcode.cn/problems/find-the-xor-of-numbers-which-appear-twice/description/?envType=daily-question&envId=2024-10-12
    def duplicateNumbersXOR(self, nums: List[int]) -> int:
        ans = vis = 0
        for x in nums:
            if vis >> x & 1:  # x 在 vis 中
                ans ^= x
            else:
                vis |= 1 << x  # 把 x 加到 vis 中
        return ans

    # 20241013
    # https://leetcode.cn/problems/egg-drop-with-2-eggs-and-n-floors/description/?envType=daily-question&envId=2024-10-13
    def twoEggDrop(self, n: int) -> int:
        return ceil(sqrt(n * 8 + 1)) // 2

    # 20241014
    # https://leetcode.cn/problems/super-egg-drop/description/?envType=daily-question&envId=2024-10-14
    def superEggDrop(self, k: int, n: int) -> int:
        @cache
        def dfs(i: int, j: int) -> int:
            if i < 1:
                return 0
            if j == 1:
                return i
            l, r = 1, i
            while l < r:
                mid = (l + r + 1) >> 1
                a = dfs(mid - 1, j - 1)
                b = dfs(i - mid, j)
                if a <= b:
                    l = mid
                else:
                    r = mid - 1
            return max(dfs(l - 1, j - 1), dfs(i - l, j)) + 1

        return dfs(n, k)

    # 20241015
    # https://leetcode.cn/problems/maximum-height-of-a-triangle/description/?envType=daily-question&envId=2024-10-15
    def maxHeightOfTriangle(self, red: int, blue: int) -> int:
        cnt = [0, 0]
        for i in count(1):
            cnt[i % 2] += i
            if (cnt[0] > red or cnt[1] > blue) and (cnt[0] > blue or cnt[1] > red):
                return i - 1

    # 20241016
    # https://leetcode.cn/problems/minimum-average-of-smallest-and-largest-elements/description/?envType=daily-question&envId=2024-10-16
    def minimumAverage(self, nums: List[int]) -> float:
        nums.sort()
        return min(nums[i] + nums[-1 - i] for i in range(len(nums) // 2)) / 2

    # 20241017
    # https://leetcode.cn/problems/count-the-number-of-inversions/description/?envType=daily-question&envId=2024-10-17
    def numberOfPermutations(self, n: int, requirements: List[List[int]]) -> int:
        MOD = 1_000_000_007
        req = [-1] * n
        req[0] = 0
        for end, cnt in requirements:
            req[end] = cnt
        if req[0]:
            return 0

        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, j: int) -> int:
            if i == 0:
                return 1
            r = req[i - 1]
            if r >= 0:
                return dfs(i - 1, r) if r <= j <= i + r else 0
            return sum(dfs(i - 1, j - k) for k in range(min(i, j) + 1)) % MOD
        return dfs(n - 1, req[-1])

    # 20241018
    # https://leetcode.cn/problems/minimum-operations-to-make-binary-array-elements-equal-to-one-i/description/?envType=daily-question&envId=2024-10-18
    def minOperations(self, nums: List[int]) -> int:
        ans = 0
        for i in range(len(nums) - 2):
            if nums[i] == 0:  # 必须操作
                nums[i + 1] ^= 1
                nums[i + 2] ^= 1
                ans += 1
        return ans if nums[-2] and nums[-1] else -1

    # 20241019
    # https://leetcode.cn/problems/minimum-operations-to-make-binary-array-elements-equal-to-one-ii/description/?envType=daily-question&envId=2024-10-19
    def minOperations(self, nums: List[int]) -> int:
        k = 0
        for x in nums:
            if x == k % 2:  # 必须操作
                k += 1
        return k

    # 20241020
    # https://leetcode.cn/problems/smallest-range-i/description/?envType=daily-question&envId=2024-10-20
    def smallestRangeI(self, nums: List[int], k: int) -> int:
        return max(max(nums) - min(nums) - k * 2, 0)

    # 20241021
    # https://leetcode.cn/problems/smallest-range-ii/description/?envType=daily-question&envId=2024-10-21
    def smallestRangeII(self, nums: List[int], k: int) -> int:
        nums.sort()
        ans = nums[-1] - nums[0]
        for x, y in pairwise(nums):
            mx = max(x + k, nums[-1] - k)
            mn = min(nums[0] + k, y - k)
            ans = min(ans, mx - mn)
        return ans

    # 20241022
    # https://leetcode.cn/problems/count-pairs-that-form-a-complete-day-i/description/?envType=daily-question&envId=2024-10-22
    def countCompleteDayPairs(self, hours: List[int]) -> int:
        ans = 0
        for i in range(1, len(hours)):
            for j in range(i):
                if (hours[i] + hours[j]) % 24 == 0:
                    ans += 1
        return ans
    
    # 20241023
    # https://leetcode.cn/problems/count-pairs-that-form-a-complete-day-ii/description/?envType=daily-question&envId=2024-10-23
    def countCompleteDayPairs(self, hours: List[int]) -> int:
        H = 24
        ans = 0
        cnt = [0] * H
        for t in hours:
            # 先查询 cnt，再更新 cnt，因为题目要求 i < j
            # 如果先更新，再查询，就把 i = j 的情况也考虑进去了
            ans += cnt[(H - t % H) % H]
            cnt[t % H] += 1
        return ans

    # 20241024
    # https://leetcode.cn/problems/find-the-first-player-to-win-k-games-in-a-row/description/?envType=daily-question&envId=2024-10-24
    def findWinningPlayer(self, skills: List[int], k: int) -> int:
        n = len(skills)
        cnt = 0
        i, last_i = 0, 0
        while i < n:
            j = i + 1
            while j < n and skills[j] < skills[i] and cnt < k:
                cnt += 1
                j += 1
            if cnt == k:
                return i
            cnt = 1
            last_i = i
            i = j
        return last_i

    # 20241025
    # https://leetcode.cn/problems/maximum-total-reward-using-operations-i/description/?envType=daily-question&envId=2024-10-25
    def maxTotalReward(self, rewardValues: List[int]) -> int:
        n = len(rewardValues)
        rewardValues.sort()
        @cache
        def dfs(i, s):
            if i >= n or s > 2000:
                return 0
            j1 = bisect_right(rewardValues, rewardValues[i])
            j2 = bisect_right(rewardValues, rewardValues[i] + s)
            return max(
                dfs(j1, s),
                rewardValues[i] + dfs(j2, s + rewardValues[i])
            )
        return dfs(0, 0)

    # 20241026
    # https://leetcode.cn/problems/maximum-total-reward-using-operations-ii/description/?envType=daily-question&envId=2024-10-26
    def maxTotalReward(self, rewardValues: List[int]) -> int:
        f = 1
        for v in sorted(set(rewardValues)):
            f |= (f & ((1 << v) - 1)) << v
        return f.bit_length() - 1

    # 20241027
    # https://leetcode.cn/problems/redundant-connection/description/?envType=daily-question&envId=2024-10-27
    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
        def find(x: int) -> int:
            if p[x] != x:
                p[x] = find(p[x])
            return p[x]

        p = list(range(len(edges)))
        for a, b in edges:
            pa, pb = find(a - 1), find(b - 1)
            if pa == pb:
                return [a, b]
            p[pa] = pb

    # 20241028
    # https://leetcode.cn/problems/redundant-connection-ii/description/?envType=daily-question&envId=2024-10-28
    def findRedundantDirectedConnection(self, edges: List[List[int]]) -> List[int]:
        def find(x: int) -> int:
            if p[x] != x:
                p[x] = find(p[x])
            return p[x]

        n = len(edges)
        ind = [0] * n
        for _, v in edges:
            ind[v - 1] += 1
        dup = [i for i, (_, v) in enumerate(edges) if ind[v - 1] == 2]
        p = list(range(n))
        if dup:
            for i, (u, v) in enumerate(edges):
                if i == dup[1]:
                    continue
                pu, pv = find(u - 1), find(v - 1)
                if pu == pv:
                    return edges[dup[0]]
                p[pu] = pv
            return edges[dup[1]]
        for i, (u, v) in enumerate(edges):
            pu, pv = find(u - 1), find(v - 1)
            if pu == pv:
                return edges[i]
            p[pu] = pv

    # 20241029
    # https://leetcode.cn/problems/generate-binary-strings-without-adjacent-zeros/description/?envType=daily-question&envId=2024-10-29
    def validStrings(self, n: int) -> List[str]:
        ans = []
        path = [''] * n

        def dfs(i: int) -> None:
            if i == n:
                ans.append(''.join(path))  # 注意 join 需要 O(n) 时间
                return

            # 填 1
            path[i] = '1'
            dfs(i + 1)

            # 填 0
            if i == 0 or path[i - 1] == '1':
                path[i] = '0'  # 直接覆盖
                dfs(i + 1)

        dfs(0)
        return ans

    # 20241030
    # https://leetcode.cn/problems/lexicographically-smallest-string-after-a-swap/description/?envType=daily-question&envId=2024-10-30
    def getSmallestString(self, s: str) -> str:
        s = list(s)
        for i in range(0, len(s) - 1):
            if s[i] > s[i + 1] and ord(s[i]) % 2 == ord(s[i + 1]) % 2:
                s[i], s[i + 1] = s[i + 1], s[i]
                break
        return ''.join(s)

    # 20241031
    # https://leetcode.cn/problems/maximum-sum-of-subsequence-with-non-adjacent-elements/description/?envType=daily-question&envId=2024-10-31
    def maximumSumSubsequence(self, nums: List[int], queries: List[List[int]]) -> int:
        n = len(nums)
        # 4 个数分别保存 f00, f01, f10, f11
        t = [[0] * 4 for _ in range(2 << n.bit_length())]

        # 手写 max，效率更高
        def max(a: int, b: int) -> int:
            return b if b > a else a

        # 合并左右儿子
        def maintain(o: int):
            a, b = t[o * 2], t[o * 2 + 1]
            t[o][0] = max(a[0] + b[2], a[1] + b[0])
            t[o][1] = max(a[0] + b[3], a[1] + b[1])
            t[o][2] = max(a[2] + b[2], a[3] + b[0])
            t[o][3] = max(a[2] + b[3], a[3] + b[1])

        # 用 nums 初始化线段树
        def build(o: int, l: int, r: int) -> None:
            if l == r:
                t[o][3] = max(nums[l], 0)
                return
            m = (l + r) // 2
            build(o * 2, l, m)
            build(o * 2 + 1, m + 1, r)
            maintain(o)

        # 把 nums[i] 改成 val
        def update(o: int, l: int, r: int, i: int, val: int) -> None:
            if l == r:
                t[o][3] = max(val, 0)
                return
            m = (l + r) // 2
            if i <= m:
                update(o * 2, l, m, i, val)
            else:
                update(o * 2 + 1, m + 1, r, i, val)
            maintain(o)

        build(1, 0, n - 1)

        ans = 0
        for i, x in queries:
            update(1, 0, n - 1, i, x)
            ans += t[1][3]  # 注意 f11 没有任何限制，也就是整个数组的打家劫舍
        return ans % 1_000_000_007

    # 20241101
    # https://leetcode.cn/problems/maximum-energy-boost-from-two-drinks/description/?envType=daily-question&envId=2024-11-01
    def maxEnergyBoost(self, energyDrinkA: List[int], energyDrinkB: List[int]) -> int:
        n = len(energyDrinkA)
        f = [[0] * 2 for _ in range(n)]
        f[0][0] = energyDrinkA[0]
        f[0][1] = energyDrinkB[0]
        for i in range(1, n):
            f[i][0] = max(f[i - 1][0] + energyDrinkA[i], f[i - 1][1])
            f[i][1] = max(f[i - 1][1] + energyDrinkB[i], f[i - 1][0])
        return max(f[n - 1])

    # 20241102
    # https://leetcode.cn/problems/number-of-bit-changes-to-make-two-integers-equal/description/?envType=daily-question&envId=2024-11-02
    def minChanges(self, n: int, k: int) -> int:
        return -1 if n & k != k else (n ^ k).bit_count()

    # 20241103
    # https://leetcode.cn/problems/shopping-offers/description/?envType=daily-question&envId=2024-11-03
    def shoppingOffers(
        self, price: List[int], special: List[List[int]], needs: List[int]
    ) -> int:
        @cache
        def dfs(cur: int) -> int:
            ans = sum(p * (cur >> (i * bits) & 0xF) for i, p in enumerate(price))
            for offer in special:
                nxt = cur
                for j in range(len(needs)):
                    if (cur >> (j * bits) & 0xF) < offer[j]:
                        break
                    nxt -= offer[j] << (j * bits)
                else:
                    ans = min(ans, offer[-1] + dfs(nxt))
            return ans

        bits, mask = 4, 0
        for i, need in enumerate(needs):
            mask |= need << i * bits
        return dfs(mask)

    # 20241104
    # https://leetcode.cn/problems/sum-of-square-numbers/description/?envType=daily-question&envId=2024-11-04
    def judgeSquareSum(self, c: int) -> bool:
        a = 0
        while a * a * 2 <= c:
            b = isqrt(c - a * a)
            if a * a + b * b == c:
                return True
            a += 1
        return False

    # 20241105
    # https://leetcode.cn/problems/find-the-winning-player-in-coin-game/description/?envType=daily-question&envId=2024-11-05
    def losingPlayer(self, x: int, y: int) -> str:
        return "Alice" if min(x, y // 4) % 2 else "Bob"

    # 20241106
    # https://leetcode.cn/problems/find-the-power-of-k-size-subarrays-i/description/?envType=daily-question&envId=2024-11-06
    def resultsArray(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        ans = [-1] * (n - k + 1)
        cnt = 0  #记录子数组中满足依次连续且上升的个数
        for i, num in enumerate(nums):
            if i == 0 or nums[i - 1] + 1 == num: #满足依次连续且上升
                cnt += 1
            else:
                cnt = 1
            if cnt >= k: #子数组满足依次连续且上升，更新答案
                ans[i - k + 1] = num 
                #数组是上升的，所以子数组自己拍卖行最后一个数最大
        return ans

    # 20241107
    # https://leetcode.cn/problems/find-the-power-of-k-size-subarrays-ii/description/?envType=daily-question&envId=2024-11-07
    def resultsArray(self, nums: List[int], k: int) -> List[int]:
        ans = [-1] * (len(nums) - k + 1)
        cnt = 0
        for i, x in enumerate(nums):
            cnt = cnt + 1 if i == 0 or x == nums[i - 1] + 1 else 1
            if cnt >= k:
                ans[i - k + 1] = x
        return ans

    # 20241108
    # https://leetcode.cn/problems/check-if-the-rectangle-corner-is-reachable/description/?envType=daily-question&envId=2024-11-08
    def canReachCorner(self, X: int, Y: int, circles: List[List[int]]) -> bool:
        # 判断点 (x,y) 是否在圆 (ox,oy,r) 内
        def in_circle(ox: int, oy: int, r: int, x: int, y: int) -> bool:
            return (ox - x) * (ox - x) + (oy - y) * (oy - y) <= r * r

        vis = [False] * len(circles)
        def dfs(i: int) -> bool:
            x1, y1, r1 = circles[i]
            # 圆 i 是否与矩形右边界/下边界相交相切
            if y1 <= Y and abs(x1 - X) <= r1 or \
               x1 <= X and y1 <= r1 or \
               x1 > X and in_circle(x1, y1, r1, X, 0):
                return True
            vis[i] = True
            for j, (x2, y2, r2) in enumerate(circles):
                # 在两圆相交相切的前提下，点 A 是否严格在矩形内
                if not vis[j] and \
                   (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) <= (r1 + r2) * (r1 + r2) and \
                   x1 * r2 + x2 * r1 < (r1 + r2) * X and \
                   y1 * r2 + y2 * r1 < (r1 + r2) * Y and \
                   dfs(j):
                    return True
            return False

        for i, (x, y, r) in enumerate(circles):
            # 圆 i 包含矩形左下角 or
            # 圆 i 包含矩形右上角 or
            # 圆 i 与矩形上边界/左边界相交相切
            if in_circle(x, y, r, 0, 0) or \
               in_circle(x, y, r, X, Y) or \
               not vis[i] and (x <= X and abs(y - Y) <= r or
                               y <= Y and x <= r or
                               y > Y and in_circle(x, y, r, 0, Y)) and dfs(i):
                return False
        return True

    # 20241110
    # https://leetcode.cn/problems/single-element-in-a-sorted-array/description/?envType=daily-question&envId=2024-11-10
    def singleNonDuplicate(self, nums: List[int]) -> int:
        left, right = -1, len(nums) // 2
        while left + 1 < right:
            mid = (left + right) // 2
            if nums[mid * 2] != nums[mid * 2 + 1]:
                right = mid
            else:
                left = mid
        return nums[right * 2]

    # 20241111
    # https://leetcode.cn/problems/minimum-cost-to-cut-a-stick/description/?envType=daily-question&envId=2024-11-11
    def minCost(self, n: int, cuts: List[int]) -> int:
        cuts.sort()
        cuts = [0] + cuts + [n]

        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, j: int) -> int:
            if i + 1 == j:  # 无需切割
                return 0
            # 枚举切割位置 cuts[k]
            return min(dfs(i, k) + dfs(k, j) for k in range(i + 1, j)) + cuts[j] - cuts[i]

        return dfs(0, len(cuts) - 1)

    # 20241112
    # https://leetcode.cn/problems/count-substrings-that-satisfy-k-constraint-i/description/?envType=daily-question&envId=2024-11-12
    def countKConstraintSubstrings(self, s: str, k: int) -> int:
        ans = left = 0
        cnt = [0, 0]
        for i, c in enumerate(s):
            cnt[ord(c) & 1] += 1
            while cnt[0] > k and cnt[1] > k:
                cnt[ord(s[left]) & 1] -= 1
                left += 1
            ans += i - left + 1
        return ans

    # 20241113
    # https://leetcode.cn/problems/count-substrings-that-satisfy-k-constraint-ii/description/?envType=daily-question&envId=2024-11-13
    def countKConstraintSubstrings(self, s: str, k: int, queries: List[List[int]]) -> List[int]:
        n = len(s)
        left = [0] * n
        pre = [0] * (n + 1)
        cnt = [0, 0]
        l = 0
        for i, c in enumerate(s):
            cnt[ord(c) & 1] += 1
            while cnt[0] > k and cnt[1] > k:
                cnt[ord(s[l]) & 1] -= 1
                l += 1
            left[i] = l  # 记录合法子串右端点 i 对应的最小左端点 l
            # 计算 i-left[i]+1 的前缀和
            pre[i + 1] = pre[i] + i - l + 1

        ans = []
        for l, r in queries:
            j = bisect_left(left, l, l, r + 1)  # 如果区间内所有数都小于 l，结果是 j=r+1
            ans.append(pre[r + 1] - pre[j] + (j - l + 1) * (j - l) // 2)
        return ans

    # 20241114
    # https://leetcode.cn/problems/count-the-number-of-good-nodes/description/?envType=daily-question&envId=2024-11-14
    def countGoodNodes(self, edges: List[List[int]]) -> int:
        n = len(edges) + 1
        g = [[] for _ in range(n)]
        for x, y in edges:
            g[x].append(y)
            g[y].append(x)

        ans = 0
        def dfs(x: int, fa: int) -> int:
            size, sz0, ok = 1, 0, True
            for y in g[x]:
                if y == fa:
                    continue  # 不能递归到父节点
                sz = dfs(y, x)
                if sz0 == 0:
                    sz0 = sz  # 记录第一个儿子子树的大小
                elif sz != sz0:  # 存在大小不一样的儿子子树
                    ok = False  # 注意不能 break，其他子树 y 仍然要递归
                size += sz
            nonlocal ans
            ans += ok
            return size
        dfs(0, -1)
        return ans

    # 20241115
    # https://leetcode.cn/problems/minimum-number-of-flips-to-make-binary-grid-palindromic-i/description/?envType=daily-question&envId=2024-11-15
    def minFlips(self, grid: List[List[int]]) -> int:
        diff_row = 0
        for row in grid:
            for j in range(len(row) // 2):
                if row[j] != row[-1 - j]:
                    diff_row += 1

        diff_col = 0
        for col in zip(*grid):
            for i in range(len(grid) // 2):
                if col[i] != col[-1 - i]:
                    diff_col += 1

        return min(diff_row, diff_col)
    
    # 20241116
    # https://leetcode.cn/problems/minimum-number-of-flips-to-make-binary-grid-palindromic-ii/description/?envType=daily-question&envId=2024-11-16
    def minFlips(self, a: List[List[int]]) -> int:
        m, n = len(a), len(a[0])
        ans = 0
        for i in range(m // 2):
            row, row2 = a[i], a[-1 - i]
            for j in range(n // 2):
                cnt1 = row[j] + row[-1 - j] + row2[j] + row2[-1 - j]
                ans += min(cnt1, 4 - cnt1)  # 全为 1 或全为 0

        if m % 2 and n % 2:
            # 正中间的数必须是 0
            ans += a[m // 2][n // 2]

        diff = cnt1 = 0
        if m % 2:
            # 统计正中间这一排
            row = a[m // 2]
            for j in range(n // 2):
                if row[j] != row[-1 - j]:
                    diff += 1
                else:
                    cnt1 += row[j] * 2
        if n % 2:
            # 统计正中间这一列
            for i in range(m // 2):
                if a[i][n // 2] != a[-1 - i][n // 2]:
                    diff += 1
                else:
                    cnt1 += a[i][n // 2] * 2

        return ans + (diff if diff else cnt1 % 4)

    # 20241117
    # https://leetcode.cn/problems/friends-of-appropriate-ages/description/?envType=daily-question&envId=2024-11-17
    def numFriendRequests(self, ages: List[int]) -> int:
        cnt = [0] * 121
        for age in ages:
            cnt[age] += 1

        ans = cnt_window = age_y = 0
        for age_x, c in enumerate(cnt):
            cnt_window += c
            if age_y * 2 <= age_x + 14:  # 不能发送好友请求
                cnt_window -= cnt[age_y]
                age_y += 1
            if cnt_window:  # 存在可以发送好友请求的用户
                ans += c * cnt_window - c
        return ans

    # 20241118
    # https://leetcode.cn/problems/image-smoother/description/?envType=daily-question&envId=2024-11-18
    def imageSmoother(self, img: List[List[int]]) -> List[List[int]]:
        m, n = len(img), len(img[0])
        ans = [[0] * n for _ in range(m)]
        for i in range(0, m):
            for j in range(0, n):
                lst = [(i_, j_) for i_ in range(max(i - 1, 0), min(i + 2, m)) for j_ in range(max(j - 1, 0), min(j + 2, n))]
                ans[i][j] = sum(img[i_][j_] for i_, j_ in lst) // len(lst)
        return ans

    # 20241119
    # https://leetcode.cn/problems/shortest-distance-after-road-addition-queries-i/description/?envType=daily-question&envId=2024-11-19
    def shortestDistanceAfterQueries(self, n: int, queries: List[List[int]]) -> List[int]:
        g = [[i + 1] for i in range(n - 1)]
        vis = [-1] * (n - 1)

        def bfs(i: int) -> int:
            q = deque([0])
            for step in count(1):
                tmp = q
                q = []
                for x in tmp:
                    for y in g[x]:
                        if y == n - 1:
                            return step
                        if vis[y] != i:
                            vis[y] = i
                            q.append(y)
            return -1

        ans = [0] * len(queries)
        for i, (l, r) in enumerate(queries):
            g[l].append(r)
            ans[i] = bfs(i)
        return ans

    # 20241120
    # https://leetcode.cn/problems/shortest-distance-after-road-addition-queries-ii/description/?envType=daily-question&envId=2024-11-20
    def shortestDistanceAfterQueries(self, n: int, queries: List[List[int]]) -> List[int]:
        fa = list(range(n - 1))

        # 非递归并查集
        def find(x: int) -> int:
            rt = x
            while fa[rt] != rt:
                rt = fa[rt]
            while fa[x] != rt:
                fa[x], x = rt, fa[x]
            return rt

        ans = []
        cnt = n - 1  # 并查集连通块个数
        for l, r in queries:
            fr = find(r - 1)
            i = find(l)
            while i < r - 1:
                cnt -= 1
                fa[i] = fr
                i = find(i + 1)
            ans.append(cnt)
        return ans

    # 20241121
    # https://leetcode.cn/problems/snake-in-matrix/description/?envType=daily-question&envId=2024-11-21
    def finalPositionOfSnake(self, n: int, commands: List[str]) -> int:
        i = j = 0
        for s in commands:
            if s[0] == 'U': i -= 1
            elif s[0] == 'D': i += 1
            elif s[0] == 'L': j -= 1
            else: j += 1
        return i * n + j
    
    # 20241122
    # https://leetcode.cn/problems/find-the-count-of-numbers-which-are-not-special/description/?envType=daily-question&envId=2024-11-22
    def nonSpecialCount(self, left: int, right: int) -> int:
        result = 0
        maxVal = int(math.sqrt(right)) + 1
        isPrime = [True] * maxVal
        
        for i in range(2, maxVal):
            if isPrime[i] and i * i >= left and i * i <= right:
                result += 1
                
            for j in range(i * 2, maxVal, i):
                isPrime[j] = False
        
        return right - left + 1 - result
    
    # 20241123
    # https://leetcode.cn/problems/find-the-number-of-winning-players/description/?envType=daily-question&envId=2024-11-23
    def winningPlayerCount(self, n: int, pick: List[List[int]]) -> int:
        cnts = [[0] * 11 for _ in range(n)]
        for x, y in pick:
            cnts[x][y] += 1

        ans = 0
        for i, cnt in enumerate(cnts):
            if any(c > i for c in cnt):
                ans += 1
        return ans

    # 20241124
    # https://leetcode.cn/problems/smallest-range-covering-elements-from-k-lists/description/?envType=daily-question&envId=2024-11-24
    def smallestRange(self, nums: List[List[int]]) -> List[int]:
        # 把每个列表的第一个元素入堆
        h = [(arr[0], i, 0) for i, arr in enumerate(nums)]
        heapify(h)

        ans_l = h[0][0]  # 第一个合法区间的左端点
        ans_r = r = max(arr[0] for arr in nums)  # 第一个合法区间的右端点
        while h[0][2] + 1 < len(nums[h[0][1]]):  # 堆顶列表有下一个元素
            _, i, j = h[0]
            x = nums[i][j + 1]  # 堆顶列表的下一个元素
            heapreplace(h, (x, i, j + 1))  # 替换堆顶
            r = max(r, x)  # 更新合法区间的右端点
            l = h[0][0]  # 当前合法区间的左端点
            if r - l < ans_r - ans_l:
                ans_l, ans_r = l, r
        return [ans_l, ans_r]

    # 20241125
    # https://leetcode.cn/problems/network-delay-time/description/?envType=daily-question&envId=2024-11-25
    def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
        g = [[inf for _ in range(n)] for _ in range(n)]  # 邻接矩阵
        for x, y, d in times:
            g[x - 1][y - 1] = d

        dis = [inf] * n
        ans = dis[k - 1] = 0
        done = [False] * n
        while True:
            x = -1
            for i, ok in enumerate(done):
                if not ok and (x < 0 or dis[i] < dis[x]):
                    x = i
            if x < 0:
                return ans  # 最后一次算出的最短路就是最大的
            if dis[x] == inf:  # 有节点无法到达
                return -1
            ans = dis[x]  # 求出的最短路会越来越大
            done[x] = True  # 最短路长度已确定（无法变得更小）
            for y, d in enumerate(g[x]):
                # 更新 x 的邻居的最短路
                dis[y] = min(dis[y], dis[x] + d)

    # 20241126
    # https://leetcode.cn/problems/alternating-groups-i/description/?envType=daily-question&envId=2024-11-26
    def numberOfAlternatingGroups(self, colors: List[int]) -> int:
        k = 3
        n = len(colors)
        ans = cnt = 0
        for i in range(n << 1):
            if i and colors[i % n] == colors[(i - 1) % n]:
                cnt = 1
            else:
                cnt += 1
            ans += i >= n and cnt >= k
        return ans

    # 20241127
    # https://leetcode.cn/problems/alternating-groups-ii/description/?envType=daily-question&envId=2024-11-27
    def numberOfAlternatingGroups(self, colors: List[int], k: int) -> int:
        n = len(colors)
        ans = cnt = 0
        for i in range(n * 2):
            if i > 0 and colors[i % n] == colors[(i - 1) % n]:
                cnt = 0
            cnt += 1
            if i >= n and cnt >= k:
                ans += 1
        return ans

    # 20241128
    # https://leetcode.cn/problems/find-the-count-of-monotonic-pairs-i/description/?envType=daily-question&envId=2024-11-28
    def countOfPairs(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [[0] * 51 for i in range(n)]
        mod = 10 ** 9 + 7
        for v in range(nums[0] + 1):
            dp[0][v] = 1
        for i in range(1, n):
            for v2 in range(nums[i] + 1):
                for v1 in range(v2 + 1):
                    if nums[i - 1] - v1 >= nums[i] - v2 >= 0:
                        dp[i][v2] = (dp[i][v2] + dp[i - 1][v1]) % mod
        return sum(dp[n - 1]) % mod

    # 20241129
    # https://leetcode.cn/problems/find-the-count-of-monotonic-pairs-ii/description/?envType=daily-question&envId=2024-11-29
    def countOfPairs(self, nums: List[int]) -> int:
        MOD = 1_000_000_007
        n = len(nums)
        m = max(nums)
        f = [[0] * (m + 1) for _ in range(n)]
        for j in range(nums[0] + 1):
            f[0][j] = 1
        for i in range(1, n):
            s = list(accumulate(f[i - 1]))  # f[i-1] 的前缀和
            for j in range(nums[i] + 1):
                max_k = j + min(nums[i - 1] - nums[i], 0)
                f[i][j] = s[max_k] % MOD if max_k >= 0 else 0
        return sum(f[-1][:nums[-1] + 1]) % MOD

    # 20241130
    # https://leetcode.cn/problems/find-if-digit-game-can-be-won/description/?envType=daily-question&envId=2024-11-30
    def canAliceWin(self, nums: List[int]) -> bool:
        single_digit_sum = 0
        double_digit_sum = 0
        for num in nums:
            if num < 10:
                single_digit_sum += num
            else:
                double_digit_sum += num
        return single_digit_sum != double_digit_sum

    # 20241202
    # https://leetcode.cn/problems/n-queens-ii/description/?envType=daily-question&envId=2024-12-02
    def totalNQueens(self, n: int) -> int:
        ans = 0
        col = [False] * n
        diag1 = [False] * (n * 2 - 1)
        diag2 = [False] * (n * 2 - 1)
        def dfs(r: int) -> None:
            if r == n:
                nonlocal ans
                ans += 1  # 找到一个合法方案
                return
            for c, ok in enumerate(col):
                if not ok and not diag1[r + c] and not diag2[r - c]:
                    col[c] = diag1[r + c] = diag2[r - c] = True
                    dfs(r + 1)
                    col[c] = diag1[r + c] = diag2[r - c] = False  # 恢复现场
        dfs(0)
        return ans

    # 20241203
    # https://leetcode.cn/problems/check-if-two-chessboard-squares-have-the-same-color/description/?envType=daily-question&envId=2024-12-03
    def checkTwoChessboards(self, s: str, t: str) -> bool:
        return (ord(s[0]) + ord(s[1])) % 2 == (ord(t[0]) + ord(t[1])) % 2

    # 20241204
    # https://leetcode.cn/problems/number-of-valid-move-combinations-on-chessboard/description/?envType=daily-question&envId=2024-12-04
    def countCombinations(self, pieces: List[str], positions: List[List[int]]) -> int:
        def dfs(idx):#棋子下标
            if idx == l:#如果所有棋子都已落子，此方案可行，返回1
                return 1
            i, j = positions[idx]#棋子的初始位置
            res = 0
            if all(visit[i][j][k] == 0 for k in range(8)):#如果棋子当前位置上所有时间状态位都不存在棋子才可以落子，否则会相遇。
                for k in range(8):#回溯标记，由于棋子不动，所以8个状态位都是满的
                    visit[i][j][k] = 1
                res += dfs(idx + 1)#继续搜索
                for k in range(8):#取消标记
                    visit[i][j][k] = 0
            if pieces[idx] == "rook":#根据棋子选择棋子的方向
                it = [[1, 0], [0, 1], [-1, 0], [0, -1]]
            elif pieces[idx] == "bishop":
                it = [[1, 1], [1, -1], [-1, 1], [-1, -1]]
            else:
                it = [[1, 0], [0, 1], [-1, 0], [0, -1], [1, 1], [1, -1], [-1, 1], [-1, -1]]
            for dx, dy in it:#遍历所有的方向
                x, y, c = i, j, 0 #分别是起始位置和时间状态
                tmp = [] #用于记录行进路径
                while 0 <= x + dx < 8 and 0 <= y + dy < 8 and visit[x + dx][y + dy][c] == 0: #需要满足移动的新位置的时间状态下不存在棋子
                    x += dx
                    y += dy
                    c += 1
                    visit[x][y][c - 1] = 1#移动后需要标记当前时间状态
                    tmp.append([x, y, c - 1])#移动状态会维持到落子之后，需要在此方向遍历结束后才能取消标记，所以在这里先记录
                    if all(visit[x][y][k] == 0 for k in range(c, 8)):#如果之后的时间状态也不存在棋子，所以可以落子
                        for k in range(c, 8):#标记后续状态
                            visit[x][y][k] = 1
                        res += dfs(idx + 1)#继续搜索
                        for k in range(c, 8):#取消标记
                            visit[x][y][k] = 0
                for x, y, k in tmp:#将行进路径上的所有状态取消标记
                    visit[x][y][k] = 0
            return res
        l = len(pieces)
        positions = [[i - 1, j - 1] for i, j in positions]#坐标-1，方便计算
        visit = [[[0 for i in range(8)] for j in range(8)] for k in range(8)]#visit标记
        return dfs(0)

    # 20241205
    # https://leetcode.cn/problems/minimum-moves-to-capture-the-queen/description/?envType=daily-question&envId=2024-12-05
    def minMovesToCaptureTheQueen(self, a: int, b: int, c: int, d: int, e: int, f: int) -> int:
        # m 在 l 和 r 之间（写不写等号都可以）
        def in_between(l: int, m: int, r: int) -> bool:
            return min(l, r) <= m <= max(l, r)

        # 车直接攻击到皇后 or 象直接攻击到皇后
        if a == e and (c != e or not in_between(b, d, f)) or \
           b == f and (d != f or not in_between(a, c, e)) or \
           c + d == e + f and (a + b != e + f or not in_between(c, a, e)) or \
           c - d == e - f and (a - b != e - f or not in_between(c, a, e)):
            return 1
        return 2

    # 20241206
    # https://leetcode.cn/problems/available-captures-for-rook/description/?envType=daily-question&envId=2024-12-06
    def numRookCaptures(self, board: List[List[str]]) -> int:
        SIZE = 8
        for i, row in enumerate(board):
            for j, c in enumerate(row):
                if c == 'R':
                    x0, y0 = i, j
        ans = 0
        for dx, dy in (0, -1), (0, 1), (-1, 0), (1, 0):
            x, y = x0 + dx, y0 + dy
            while 0 <= x < SIZE and 0 <= y < SIZE and board[x][y] == '.':
                x += dx
                y += dy
            if 0 <= x < SIZE and 0 <= y < SIZE and board[x][y] == 'p':
                ans += 1
        return ans

    # 20241207
    # https://leetcode.cn/problems/knight-probability-in-chessboard/description/?envType=daily-question&envId=2024-12-07
    def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
        f = [[[0] * n for _ in range(n)] for _ in range(k + 1)]
        for i in range(n):
            for j in range(n):
                f[0][i][j] = 1
        for h in range(1, k + 1):
            for i in range(n):
                for j in range(n):
                    for a, b in pairwise((-2, -1, 2, 1, -2, 1, 2, -1, -2)):
                        x, y = i + a, j + b
                        if 0 <= x < n and 0 <= y < n:
                            f[h][i][j] += f[h - 1][x][y] / 8
        return f[k][row][column]

    # 20241208
    # https://leetcode.cn/problems/transform-to-chessboard/description/?envType=daily-question&envId=2024-12-08
    def movesToChessboard(self, board: List[List[int]]) -> int:
        # 第一行，0 和 1 的个数之差不能超过 1
        first_row = board[0]
        row_cnt = Counter(first_row)
        if abs(row_cnt[0] - row_cnt[1]) > 1:
            return -1

        # 第一列，0 和 1 的个数之差不能超过 1
        first_col = list(next(zip(*board)))
        col_cnt = Counter(first_col)
        if abs(col_cnt[0] - col_cnt[1]) > 1:
            return -1

        # 每一行和第一行比较，要么完全相同，要么完全不同
        for row in board:
            same = row[0] == first_row[0]
            for x, y in zip(row, first_row):
                if (x == y) != same:
                    return -1

        # 计算最小交换次数
        def min_swap(arr: List[int], cnt: Counter) -> int:
            n = len(arr)
            x0 = 1 if cnt[1] > cnt[0] else 0  # 如果 n 是偶数，x0 是 0
            diff = sum(i % 2 ^ x ^ x0 for i, x in enumerate(arr))
            return diff // 2 if n % 2 else min(diff, n - diff) // 2

        return min_swap(first_row, row_cnt) + min_swap(first_col, col_cnt)

    # 20241209
    # https://leetcode.cn/problems/determine-color-of-a-chessboard-square/description/?envType=daily-question&envId=2024-12-09
    def squareIsWhite(self, coordinates: str) -> bool:
        return (ord(coordinates[0]) - ord('a') + 1 + int(coordinates[1])) % 2 == 1

    # 20241210
    # https://leetcode.cn/problems/knight-dialer/description/?envType=daily-question&envId=2024-12-10
    def knightDialer(self, N: int) -> int:
        if N==1: return 10
        #分别为状态A,B,C,D
        nums=[1,1,1,1]
        for _ in range(N-1):
            nums=[nums[1]+nums[2], 2*nums[0], 2*nums[0]+nums[3], 2*nums[2]]
        #状态A有4个数字，B有2个数字，C有2个数字，D有1个数字
        return (4*nums[0]+2*nums[1]+2*nums[2]+nums[3])%1000000007

    # 20241211
    # https://leetcode.cn/problems/semi-ordered-permutation/description/?envType=daily-question&envId=2024-12-11
    def semiOrderedPermutation(self, nums: List[int]) -> int:
        n = len(nums)
        p = nums.index(1)
        q = nums.index(n)
        return p + n - 1 - q - (p > q)

    # 20241212
    # https://leetcode.cn/problems/maximum-spending-after-buying-items/description/?envType=daily-question&envId=2024-12-12
    def maxSpending(self, values: List[List[int]]) -> int:
        h = [(a[-1], i) for i, a in enumerate(values)]
        heapify(h)
        ans = 0
        for d in range(1, len(values) * len(values[0]) + 1):
            v, i = heappop(h)
            ans += v * d
            values[i].pop()
            if values[i]:
                heappush(h, (values[i][-1], i))
        return ans

    # 20241213
    # https://leetcode.cn/problems/final-array-state-after-k-multiplication-operations-i/description/?envType=daily-question&envId=2024-12-13
    def getFinalState(self, nums: List[int], k: int, multiplier: int) -> List[int]:
        if multiplier == 1:  # 数组不变
            return nums

        MOD = 1_000_000_007
        n = len(nums)
        mx = max(nums)
        h = [(x, i) for i, x in enumerate(nums)]
        heapify(h)

        # 模拟，直到堆顶是 mx
        while k and h[0][0] < mx:
            x, i = h[0]
            heapreplace(h, (x * multiplier, i))
            k -= 1

        # 剩余的操作可以直接用公式计算
        h.sort()
        for i, (x, j) in enumerate(h):
            nums[j] = x * pow(multiplier, k // n + (i < k % n), MOD) % MOD
        return nums

    # 20241214
    # https://leetcode.cn/problems/final-array-state-after-k-multiplication-operations-ii/description/?envType=daily-question&envId=2024-12-14
    def getFinalState(self, nums: List[int], k: int, multiplier: int) -> List[int]:
        if multiplier == 1:  # 数组不变
            return nums

        MOD = 1_000_000_007
        n = len(nums)
        mx = max(nums)
        h = [(x, i) for i, x in enumerate(nums)]
        heapify(h)

        # 模拟，直到堆顶是 mx
        while k and h[0][0] < mx:
            x, i = h[0]
            heapreplace(h, (x * multiplier, i))
            k -= 1

        # 剩余的操作可以直接用公式计算
        h.sort()
        for i, (x, j) in enumerate(h):
            nums[j] = x * pow(multiplier, k // n + (i < k % n), MOD) % MOD
        return nums
    
    # 20241215
    # https://leetcode.cn/problems/reduce-array-size-to-the-half/description/?envType=daily-question&envId=2024-12-15
    def minSetSize(self, arr: List[int]) -> int:
        cnt = sorted(Counter(arr).values(), reverse=True)
        m = len(arr) // 2
        for i, s in enumerate(accumulate(cnt)):
            if s >= m:
                return i + 1

    # 20241216
    # https://leetcode.cn/problems/closest-room/description/?envType=daily-question&envId=2024-12-16
    def closestRoom(self, rooms: List[List[int]], queries: List[List[int]]) -> List[int]:
        rooms.sort(key=lambda a: a[1], reverse=True)
        idx = [i for i in range(len(queries))]
        idx.sort(key=lambda a: queries[a][1], reverse=True)
        ans = [-1] * len(queries)
        sl = SortedList()
        j = 0
        for i in range(len(queries)):
            preferredj, minSizej = queries[idx[i]]
            while j < len(rooms) and rooms[j][1] >= minSizej:
                sl.add(rooms[j][0])
                j += 1
            if len(sl) == 0:
                continue
            b = bisect_left(sl, preferredj,hi=len(sl) - 1)
            ans[idx[i]] = sl[b] 
            if b > 0 and abs(sl[b-1] - preferredj) <= abs(sl[b] - preferredj):
                ans[idx[i]] = sl[b-1]
        return ans
    
    # 20241217
    # https://leetcode.cn/problems/minimum-number-of-valid-strings-to-form-target-i/description/?envType=daily-question&envId=2024-12-17
    def minValidStrings(self, words: List[str], target: str) -> int:

        def kmp_next(s: str):
            n = len(s)
            next = [0] * n
            l = 0
            for r in range(1, n):
                while l > 0 and s[l] != s[r]:
                    l = next[l - 1]
                if s[l] == s[r]:
                    l += 1
                next[r] = l
            return next

        m = len(words)
        limit = len(target)
        dp = [[0] * m for _ in range(limit)]

        for i, word in enumerate(words):
            next = kmp_next(word)
            l = 0
            for j, v in enumerate(target):
                while l > 0 and (l == len(word) or word[l] != v):
                    l = next[l - 1]
                if word[l] == v:
                    l += 1
                dp[j][i] = l
        f = [inf] * limit
        for i in range(limit):
            for j in range(m):
                k = i - dp[i][j]
                f[i] = min(f[i], f[k] + 1 if k > -1 else 1)
        return f[limit - 1] if f[limit - 1] < inf else -1

    # 20241218
    # https://leetcode.cn/problems/minimum-number-of-valid-strings-to-form-target-ii/description/?envType=daily-question&envId=2024-12-18
    def calc_z(self, s: str) -> list[int]:
        n = len(s)
        z = [0] * n
        box_l = box_r = 0  # z-box 左右边界（闭区间）
        for i in range(1, n):
            if i <= box_r:
                z[i] = min(z[i - box_l], box_r - i + 1)
            while i + z[i] < n and s[z[i]] == s[i + z[i]]:
                box_l, box_r = i, i + z[i]
                z[i] += 1
        return z

    # 桥的概念，见我在 45 或 1326 题下的题解
    def jump(self, max_jumps: List[int]) -> int:
        ans = 0
        cur_r = 0  # 已建造的桥的右端点
        nxt_r = 0  # 下一座桥的右端点的最大值
        for i, max_jump in enumerate(max_jumps):  # 如果走到 n-1 时没有返回 -1，那么必然可以到达 n
            nxt_r = max(nxt_r, i + max_jump)
            if i == cur_r:  # 到达已建造的桥的右端点
                if i == nxt_r:  # 无论怎么造桥，都无法从 i 到 i+1
                    return -1
                cur_r = nxt_r  # 造一座桥
                ans += 1
        return ans

    def minValidStrings(self, words: List[str], target: str) -> int:
        n = len(target)
        max_jumps = [0] * n
        for word in words:
            z = self.calc_z(word + "#" + target)
            m = len(word) + 1
            for i in range(n):
                max_jumps[i] = max(max_jumps[i], z[m + i])
        return self.jump(max_jumps)

    # 20241219
    # https://leetcode.cn/problems/find-indices-of-stable-mountains/description/?envType=daily-question&envId=2024-12-19
    def stableMountains(self, height: List[int], threshold: int) -> List[int]:
        return [i for i, h in enumerate(height[:-1], 1) if h > threshold]

    # 20241220
    # https://leetcode.cn/problems/minimum-length-of-anagram-concatenation/description/?envType=daily-question&envId=2024-12-20
    def minAnagramLength(self, s: str) -> int:
        n = len(s)
        for i in range(1, n // 2 + 1):
            if n % i:
                continue
            cnt = Counter(s[:i])
            flag = True
            for k in range(i * 2, n + 1, i):
                if cnt != Counter(s[k - i : k]):
                    flag = False
            if flag:
                return i
        return n

    # 20241221
    # https://leetcode.cn/problems/sort-the-students-by-their-kth-score/description/?envType=daily-question&envId=2024-12-21
    def sortTheStudents(self, score: List[List[int]], k: int) -> List[List[int]]:
        score.sort(key=lambda row: -row[k])
        return score

    # 20241222
    # https://leetcode.cn/problems/sort-integers-by-the-power-value/description/?envType=daily-question&envId=2024-12-22
    def getKth(self, lower: int, upper: int, k: int) -> int:
        def weight(num: int) -> int:
            if num == 1:
                return 0
            return weight(num * 3 + 1) + 1 if num % 2 == 1 else weight(num // 2) + 1

        return sorted(range(lower, upper + 1), key=weight)[k - 1]
    
    # 20241224
    # https://leetcode.cn/problems/maximum-number-of-eaten-apples/description/?envType=daily-question&envId=2024-12-24
    def eatenApples(self, apples: List[int], days: List[int]) -> int:
        ans = i = 0
        h = []
        while i < len(apples) or h:
            while h and h[0][0] == i:  # 已腐烂
                heappop(h)
            if i < len(apples) and apples[i]:
                heappush(h, [i + days[i], apples[i]])
            if h:
                ans += 1
                h[0][1] -= 1  # 吃一个最早腐烂的苹果
                if h[0][1] == 0:
                    heappop(h)
            i += 1
        return ans

    # 20241225
    # https://leetcode.cn/problems/minimum-cost-for-cutting-cake-i/description/?envType=daily-question&envId=2024-12-25
    def minimumCost(
        self, m: int, n: int, horizontalCut: List[int], verticalCut: List[int]
    ) -> int:
        horizontalCut.sort(reverse=True)
        verticalCut.sort(reverse=True)
        ans = i = j = 0
        h = v = 1
        while i < m - 1 or j < n - 1:
            if j == n - 1 or (i < m - 1 and horizontalCut[i] > verticalCut[j]):
                ans += horizontalCut[i] * v
                h, i = h + 1, i + 1
            else:
                ans += verticalCut[j] * h
                v, j = v + 1, j + 1
        return ans

    # 20241226
    # https://leetcode.cn/problems/existence-of-a-substring-in-a-string-and-its-reverse/description/?envType=daily-question&envId=2024-12-26
    def isSubstringPresent(self, s: str) -> bool:
        for i in range(len(s) - 1):
            if s[i:i+2][::-1] in s:
                return True
        return False

    # 20241227
    # https://leetcode.cn/problems/find-occurrences-of-an-element-in-an-array/description/?envType=daily-question&envId=2024-12-27
    def occurrencesOfElement(self, nums: List[int], queries: List[int], x: int) -> List[int]:
        pos = [i for i, num in enumerate(nums) if num == x]
        return [-1 if q > len(pos) else pos[q - 1] for q in queries]

    # 20241228
    # https://leetcode.cn/problems/split-the-array/description/?envType=daily-question&envId=2024-12-28
    def isPossibleToSplit(self, nums: List[int]) -> bool:
        return max(Counter(nums).values()) <= 2

    # 20241229
    # https://leetcode.cn/problems/rank-teams-by-votes/description/?envType=daily-question&envId=2024-12-29
    def rankTeams(self, votes: List[str]) -> str:
        m = len(votes[0])
        cnts = defaultdict(lambda: [0] * m)
        for vote in votes:
            for i, ch in enumerate(vote):
                cnts[ch][i] -= 1  # 改成负数（相反数），方便比大小
        return ''.join(sorted(cnts, key=lambda ch: (cnts[ch], ch)))

    # 20241230
    # https://leetcode.cn/problems/linked-list-in-binary-tree/description/?envType=daily-question&envId=2024-12-30
    def isSubPath(self, head: ListNode, root: TreeNode) -> bool:
        def dfs(s: Optional[ListNode], t: Optional[TreeNode]) -> bool:
            if s is None:  # 整个链表匹配完毕
                return True
            # 否则需要继续匹配
            if t is None:  # 无法继续匹配
                return False
            # 节点值相同则继续匹配，否则从 head 开始重新匹配
            return s.val == t.val and (dfs(s.next, t.left) or dfs(s.next, t.right)) or \
                   s is head and (dfs(head, t.left) or dfs(head, t.right))
        return dfs(head, root)

    # 20241231
    # https://leetcode.cn/problems/minimum-cost-for-cutting-cake-ii/description/?envType=daily-question&envId=2024-12-31
    def minimumCost(self, m: int, n: int, horizontalCut: List[int], verticalCut: List[int]) -> int:
        horizontalCut.sort()
        verticalCut.sort()
        ans = i = j = 0
        for _ in range(m + n - 2):
            if j == n - 1 or i < m - 1 and horizontalCut[i] < verticalCut[j]:
                ans += horizontalCut[i] * (n - j)  # 上下连边
                i += 1
            else:
                ans += verticalCut[j] * (m - i)  # 左右连边
                j += 1
        return ans

    # 20250101
    # https://leetcode.cn/problems/convert-date-to-binary/description/?envType=daily-question&envId=2025-01-01
    def convertDateToBinary(self, date: str) -> str:
        a = date.split('-')
        for i in range(len(a)):
            a[i] = bin(int(a[i]))[2:]
        return '-'.join(a)

    # 20250106
    # https://leetcode.cn/problems/maximum-consecutive-floors-without-special-floors/description/?envType=daily-question&envId=2025-01-06
    def maxConsecutive(self, bottom: int, top: int, special: List[int]) -> int:
        special.sort()
        ans = max(special[0] - bottom, top - special[-1])
        for x, y in pairwise(special):
            ans = max(ans, y - x - 1)
        return ans

    # 20250107
    # https://leetcode.cn/problems/number-of-changing-keys/description/?envType=daily-question&envId=2025-01-07
    def countKeyChanges(self, s: str) -> int:
        return sum(x != y for x, y in pairwise(s.lower()))

    # 20250108
    # https://leetcode.cn/problems/largest-3-same-digit-number-in-string/description/?envType=daily-question&envId=2025-01-08
    def largestGoodInteger(self, num: str) -> str:
        for d in reversed(digits):
            s = d * 3
            if s in num:
                return s
        return ""

    # 20250109
    # https://leetcode.cn/problems/count-substrings-that-can-be-rearranged-to-contain-a-string-i/description/?envType=daily-question&envId=2025-01-09
    def validSubstringCount(self, word1: str, word2: str) -> int:
        count = [0] * 26
        for c in word2:
            count[ord(c) - ord('a')] += 1
        n = len(word1)
        pre_count = [[0] * 26 for _ in range(n + 1)]
        for i in range(1, n + 1):
            pre_count[i] = pre_count[i - 1][:]
            pre_count[i][ord(word1[i - 1]) - ord('a')] += 1

        def get(l, r):
            border = l
            while l < r:
                m = (l + r) // 2
                if all(pre_count[m][i] - pre_count[border - 1][i] >= count[i] for i in range(26)):
                    r = m
                else:
                    l = m + 1
            return l

        res = 0
        for l in range(1, n + 1):
            r = get(l, n + 1)
            res += n - r + 1
        return res

    # 20250110
    # https://leetcode.cn/problems/count-substrings-that-can-be-rearranged-to-contain-a-string-ii/description/?envType=daily-question&envId=2025-01-10
    def validSubstringCount(self, word1: str, word2: str) -> int:
        diff = [0] * 26
        for c in word2:
            diff[ord(c) - ord('a')] -= 1

        res = 0
        cnt = sum(1 for c in diff if c < 0)

        def update(c: int, add: int):
            nonlocal cnt
            diff[c] += add
            if add == 1 and diff[c] == 0:
                # 表明 diff[c] 由 -1 变为 0
                cnt -= 1
            elif add == -1 and diff[c] == -1:
                # 表明 diff[c] 由 0 变为 -1
                cnt += 1

        l, r = 0, 0
        while l < len(word1):
            while r < len(word1) and cnt > 0:
                update(ord(word1[r]) - ord('a'), 1)
                r += 1
            if cnt == 0:
                res += len(word1) - r + 1
            update(ord(word1[l]) - ord('a'), -1)
            l += 1

        return res

# 20221208 
# https://leetcode.cn/problems/design-an-atm-machine/
class ATM:

    def __init__(self):
        self.money = [20, 50, 100, 200 ,500]
        self.num = [0, 0, 0, 0, 0]

    def deposit(self, banknotesCount: list[int]) -> None:
        for i in range(len(banknotesCount)):
            self.num[i] += banknotesCount[i] 

    def withdraw(self, amount: int) -> list[int]:
        try_num = [0, 0, 0, 0, 0]
        index = 4
        while index >= 0:
            try_num[index] += min(amount//self.money[index], self.num[index])
            amount -= try_num[index]*self.money[index]
            index -= 1
        
        if amount != 0:
            return [-1]
        else:
            for i in range(len(self.num)):

                self.num[i] -= try_num[i] 
            return try_num

    # 20250111
    # https://leetcode.cn/problems/find-the-key-of-the-numbers/description/?envType=daily-question&envId=2025-01-11
    def generateKey(self, x: int, y: int, z: int) -> int:
        ans = 0
        pow10 = 1
        while x and y and z:
            ans += min(x % 10, y % 10, z % 10) * pow10
            x //= 10
            y //= 10
            z //= 10
            pow10 *= 10
        return ans

    # 20250112
    # https://leetcode.cn/problems/largest-combination-with-bitwise-and-greater-than-zero/description/?envType=daily-question&envId=2025-01-12
    def largestCombination(self, candidates: List[int]) -> int:
        m = max(candidates).bit_length()
        return max(sum(x >> i & 1 for x in candidates) for i in range(m))

    # 20240113
    # https://leetcode.cn/problems/number-of-ways-to-split-array/description/?envType=daily-question&envId=2025-01-13
    def waysToSplitArray(self, nums: List[int]) -> int:
        t = (sum(nums) + 1) // 2
        return sum(s >= t for s in accumulate(nums[:-1]))

    # 20250114
    # https://leetcode.cn/problems/minimum-operations-to-exceed-threshold-value-i/description/?envType=daily-question&envId=2025-01-14
    def minOperations(self, nums: List[int], k: int) -> int:
        return sum(x < k for x in nums)

    # 20250115
    # https://leetcode.cn/problems/minimum-operations-to-exceed-threshold-value-ii/description/?envType=daily-question&envId=2025-01-15
    def minOperations(self, h: List[int], k: int) -> int:
        heapify(h)
        ans = 0
        while h[0] < k:
            x = heappop(h)
            heapreplace(h, h[0] + x * 2)
            ans += 1
        return ans

    # 20250116
    # https://leetcode.cn/problems/shortest-subarray-with-or-at-least-k-i/description/?envType=daily-question&envId=2025-01-16
    def minimumSubarrayLength(self, nums: List[int], k: int) -> int:
        n = len(nums)
        cnt = [0] * 32
        ans = n + 1
        s = i = 0
        for j, x in enumerate(nums):
            s |= x
            for h in range(32):
                if x >> h & 1:
                    cnt[h] += 1
            while s >= k and i <= j:
                ans = min(ans, j - i + 1)
                y = nums[i]
                for h in range(32):
                    if y >> h & 1:
                        cnt[h] -= 1
                        if cnt[h] == 0:
                            s ^= 1 << h
                i += 1
        return -1 if ans > n else ans

    # 20250117
    # https://leetcode.cn/problems/shortest-subarray-with-or-at-least-k-ii/solutions/2716483/zi-shu-zu-orandgcd-tong-yong-mo-ban-pyth-n8xj/?envType=daily-question&envId=2025-01-17
    def minimumSubarrayLength(self, nums: List[int], k: int) -> int:
        ans = inf
        for i, x in enumerate(nums):
            if x >= k:
                return 1
            j = i - 1
            while j >= 0 and nums[j] | x != nums[j]:
                nums[j] |= x
                if nums[j] >= k:
                    ans = min(ans, i - j + 1)
                j -= 1
        return ans if ans < inf else -1

    # 20250118
    # https://leetcode.cn/problems/find-the-maximum-sequence-value-of-array/description/?envType=daily-question&envId=2025-01-18
    def maxValue(self, nums: List[int], k: int) -> int:
        mx = reduce(or_, nums)
        n = len(nums)
        suf = [None] * (n - k + 1)
        f = [[False] * (mx + 1) for _ in range(k + 1)]
        f[0][0] = True
        for i in range(n - 1, k - 1, -1):
            v = nums[i]
            # 注意当 i 比较大的时候，循环次数应和 i 有关，因为更大的 j，对应的 f[j] 全为 False
            for j in range(min(k - 1, n - 1 - i), -1, -1):
                for x, has_x in enumerate(f[j]):
                    if has_x:
                        f[j + 1][x | v] = True
            if i <= n - k:
                suf[i] = f[k].copy()

        ans = 0
        f = [[False] * (mx + 1) for _ in range(k + 1)]
        f[0][0] = True
        for i, v in enumerate(nums[:-k]):
            for j in range(min(k - 1, i), -1, -1):
                for x, has_x in enumerate(f[j]):
                    if has_x:
                        f[j + 1][x | v] = True
            if i < k - 1:
                continue
            # 这里 f[k] 就是 pre[i]
            for x, has_x in enumerate(f[k]):
                if has_x:
                    for y, has_y in enumerate(suf[i + 1]):
                        if has_y and x ^ y > ans:  # 手写 if
                            ans = x ^ y
            if ans == mx:
                return ans
        return ans

    # 20240119
    # https://leetcode.cn/problems/count-number-of-texts/description/?envType=daily-question&envId=2025-01-19
    def countTexts(self, pressedKeys: str) -> int:
        # (按键, 对应最大次数)
        cnts = {2: 3, 3: 3, 4: 3, 5: 3, 6: 3, 7: 4, 8: 3, 9: 4}
        MOD = 1_000_000_007  # 模数

        n = len(pressedKeys)
        cur_len = 1  # 当前连续相同按键的个数
        ans = 1
        
        for i in range(n):
            # 如果当前按键和下一个按键不同，或者到达最后一个按键，开始计算当前按键组合数
            if i < n - 1 and pressedKeys[i] != pressedKeys[i + 1] or i == n - 1:
                mx = cnts[int(pressedKeys[i])]  # 当前按键可以按的最大次数
                dp = [0] * (cur_len + 1)  # dp[i]表示剩余i次按键可以得到的组合数
                dp[0] = 1  # dp[0] = 1表示不再按任何按键时的一个组合
                
                for j in range(1, cur_len + 1):  # 总次数
                    for k in range(1, min(mx, j) + 1):  # 当前次数
                        dp[j] = (dp[j] + dp[j - k]) % MOD
                
                ans = (ans * dp[cur_len]) % MOD  # 乘到最终结果上
                cur_len = 1  # 重置连续按键长度
            else:
                cur_len += 1  # 如果按键相同，增加连续按键的长度
        
        return ans

    # 20250120
    # https://leetcode.cn/problems/find-closest-number-to-zero/description/?envType=daily-question&envId=2025-01-20
    def findClosestNumber(self, nums: List[int]) -> int:
        ans = nums[0]
        for x in nums:
            if abs(x) < abs(ans) or abs(x) == abs(ans) and x > 0:
                ans = x
        return ans

    # 20250121
    # https://leetcode.cn/problems/maximum-value-of-k-coins-from-piles/description/?envType=daily-question&envId=2025-01-21
    def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int:
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, j: int) -> int:
            if i < 0:
                return 0
            # 不选这一组中的任何物品
            res = dfs(i - 1, j)
            # 枚举选哪个
            for w, v in enumerate(accumulate(piles[i][:j]), 1):
                res = max(res, dfs(i - 1, j - w) + v)
            return res
        return dfs(len(piles) - 1, k)

    # 20250122
    # https://leetcode.cn/problems/maximum-number-of-coins-you-can-get/description/?envType=daily-question&envId=2025-01-22
    def maxCoins(self, piles: List[int]) -> int:
        piles.sort()
        return sum(piles[len(piles)//3::2])

    # 20250123
    # https://leetcode.cn/problems/maximum-points-after-collecting-coins-from-all-nodes/description/?envType=daily-question&envId=2025-01-23
    def maximumPoints(self, edges: List[List[int]], coins: List[int], k: int) -> int:
        g = [[] for _ in coins]
        for x, y in edges:
            g[x].append(y)
            g[y].append(x)

        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（记忆化）
        def dfs(i: int, j: int, fa: int) -> int:
            res1 = (coins[i] >> j) - k
            res2 = coins[i] >> (j + 1)
            for ch in g[i]:
                if ch != fa:
                    res1 += dfs(ch, j, i)  # 不右移
                    if j < 13:  # j+1 >= 14 相当于 res2 += 0，无需递归
                        res2 += dfs(ch, j + 1, i)  # 右移
            return max(res1, res2)

        return dfs(0, 0, -1)

    # 20250124
    # https://leetcode.cn/problems/minimum-number-of-coins-for-fruits/description/?envType=daily-question&envId=2025-01-24
    def minimumCoins(self, f: List[int]) -> int:
        n = len(f)
        for i in range((n + 1) // 2 - 1, 0, -1):
            f[i - 1] += min(f[i: i * 2 + 1])
        return f[0]

    # 20250125
    # https://leetcode.cn/problems/minimum-money-required-before-transactions/description/?envType=daily-question&envId=2025-01-25
    def minimumMoney(self, transactions: List[List[int]]) -> int:
        total_lose = mx = 0
        for cost, cashback in transactions:
            total_lose += max(cost - cashback, 0)
            mx = max(mx, min(cost, cashback))
        return total_lose + mx

    # 20250126
    # https://leetcode.cn/problems/combination-sum-ii/description/?envType=daily-question&envId=2025-01-26
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(
            state: list[int], target: int, choices: list[int], start: int, res: list[list[int]]
        ):
            """回溯算法：子集和 II"""
            # 子集和等于 target 时，记录解
            if target == 0:
                res.append(list(state))
                return
            # 遍历所有选择
            # 剪枝二：从 start 开始遍历，避免生成重复子集
            # 剪枝三：从 start 开始遍历，避免重复选择同一元素
            for i in range(start, len(choices)):
                # 剪枝一：若子集和超过 target ，则直接结束循环
                # 这是因为数组已排序，后边元素更大，子集和一定超过 target
                if target - choices[i] < 0:
                    break
                # 剪枝四：如果该元素与左边元素相等，说明该搜索分支重复，直接跳过
                if i > start and choices[i] == choices[i - 1]:
                    continue
                # 尝试：做出选择，更新 target, start
                state.append(choices[i])
                # 进行下一轮选择
                backtrack(state, target - choices[i], choices, i + 1, res)
                # 回退：撤销选择，恢复到之前的状态
                state.pop()

        state = []  # 状态（子集）
        candidates.sort()  # 对 candidates 进行排序
        start = 0  # 遍历起始点
        res = []  # 结果列表（子集列表）
        backtrack(state, target, candidates, start, res)
        return res

    # 20250127
    # https://leetcode.cn/problems/jump-game-ii/description/?envType=daily-question&envId=2025-01-27
    def jump(self, nums: List[int]) -> int:
        position = len(nums) - 1
        steps = 0
        while position > 0:
            for i in range(position):
                if i + nums[i] >= position:
                    position = i
                    steps += 1
                    break
        return steps

    # 20250128
    # https://leetcode.cn/problems/pascals-triangle-ii/description/?envType=daily-question&envId=2025-01-28
    def getRow(self, rowIndex: int) -> List[int]:
        f = [1] * (rowIndex + 1)
        for i in range(2, rowIndex + 1):
            for j in range(i - 1, 0, -1):
                f[j] += f[j - 1]
        return f

    # 20250129
    # https://leetcode.cn/problems/contains-duplicate-ii/description/?envType=daily-question&envId=2025-01-29
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        last = {}
        for i, x in enumerate(nums):
            if x in last and i - last[x] <= k:
                return True
            last[x] = i
        return False

    # 20250130
    # https://leetcode.cn/problems/intersection-of-two-arrays-ii/description/?envType=daily-question&envId=2025-01-30
    def intersect(self, nums1: [int], nums2: [int]) -> [int]:
        nums1.sort()
        nums2.sort()
        r = []
        left, right = 0, 0
        while left < len(nums1) and right < len(nums2):
            if nums1[left] < nums2[right]:
                left += 1
            elif nums1[left] == nums2[right]:
                r.append(nums1[left])
                left += 1
                right += 1    
            else:
                right += 1
        return r

    # 20250131
    # https://leetcode.cn/problems/reverse-string-ii/description/?envType=daily-question&envId=2025-01-31
    def reverseStr(self, s: str, k: int) -> str:
        s = list(s)
        for i in range(0, len(s), k * 2):
            s[i: i + k] = s[i: i + k][::-1]  # 右端点自动和 n 取最小值
        return ''.join(s)

    # 20250201
    # https://leetcode.cn/problems/search-in-rotated-sorted-array-ii/description/?envType=daily-question&envId=2025-02-01
    def search(self, nums: List[int], target: int) -> int:
        def check(i: int) -> bool:
            x = nums[i]
            if x > nums[right]:
                return target > nums[right] and x >= target
            return target > nums[right] or x >= target

        left, right = -1, len(nums) - 1  # 开区间 (-1, n-1)
        while left + 1 < right:  # 开区间不为空
            mid = (left + right) // 2
            if nums[mid] == nums[right]:
                right -= 1
            elif check(mid):
                right = mid
            else:
                left = mid
        return nums[right] == target

    # 20250202
    # https://leetcode.cn/problems/range-addition-ii/description/?envType=daily-question&envId=2025-02-02
    def maxCount(self, m: int, n: int, ops: List[List[int]]) -> int:
        min_a = min((op[0] for op in ops), default=m)
        min_b = min((op[1] for op in ops), default=n)
        return min_a * min_b

    # 20250203
    # https://leetcode.cn/problems/valid-palindrome-ii/description/?envType=daily-question&envId=2025-02-03
    def validPalindrome(self, s):
        """
        :type s: str
        :rtype: bool
        """
        isPalindrome = lambda s: s == s[::-1]
        strPart = lambda s, x: s[:x] + s[x + 1:]
        left = 0
        right = len(s) - 1
        while left < right:
            if s[left] != s[right]:
                return isPalindrome(strPart(s, left)) or isPalindrome(strPart(s, right))
            left += 1
            right -= 1
        return True

    # 20250204
    # https://leetcode.cn/problems/sort-array-by-parity-ii/description/?envType=daily-question&envId=2025-02-04
    def sortArrayByParityII(self, nums: List[int]) -> List[int]:
        i, j = 0, 1
        while i < len(nums):
            if nums[i] % 2 == 0:  # 寻找偶数下标中最左边的奇数
                i += 2
            elif nums[j] % 2 == 1:  # 寻找奇数下标中最左边的偶数
                j += 2
            else:
                nums[i], nums[j] = nums[j], nums[i]
                i += 2
                j += 2
        return nums

    # 20250205
    # https://leetcode.cn/problems/subsets-ii/description/?envType=daily-question&envId=2025-02-05
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        n = len(nums)
        ans = []
        path = []

        def dfs(i: int) -> None:
            if i == n:
                ans.append(path.copy())  # 也可以写 path[:]
                return

            # 选 x
            x = nums[i]
            path.append(x)
            dfs(i + 1)
            path.pop()  # 恢复现场

            # 不选 x，那么后面所有等于 x 的数都不选
            # 如果不跳过这些数，会导致「选 x 不选 x'」和「不选 x 选 x'」这两种情况都会加到 ans 中，这就重复了
            i += 1
            while i < n and nums[i] == x:
                i += 1
            dfs(i)

        dfs(0)
        return ans

    # 20250206
    # https://leetcode.cn/problems/permutations-ii/description/?envType=daily-question&envId=2025-02-06
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def dfs(x):
            if x == len(nums) - 1:
                res.append(list(nums))   # 添加排列方案
                return
            dic = set()
            for i in range(x, len(nums)):
                if nums[i] in dic: continue # 重复，因此剪枝
                dic.add(nums[i])
                nums[i], nums[x] = nums[x], nums[i]  # 交换，将 nums[i] 固定在第 x 位
                dfs(x + 1)                           # 开启固定第 x + 1 位元素
                nums[i], nums[x] = nums[x], nums[i]  # 恢复交换
        res = []
        dfs(0)
        return res

    # 20250207
    # https://leetcode.cn/problems/spiral-matrix-ii/description/?envType=daily-question&envId=2025-02-07
    def generateMatrix(self, n: int) -> [[int]]:
        l, r, t, b = 0, n - 1, 0, n - 1
        mat = [[0 for _ in range(n)] for _ in range(n)]
        num, tar = 1, n * n
        while num <= tar:
            for i in range(l, r + 1): # left to right
                mat[t][i] = num
                num += 1
            t += 1
            for i in range(t, b + 1): # top to bottom
                mat[i][r] = num
                num += 1
            r -= 1
            for i in range(r, l - 1, -1): # right to left
                mat[b][i] = num
                num += 1
            b -= 1
            for i in range(b, t - 1, -1): # bottom to top
                mat[i][l] = num
                num += 1
            l += 1
        return mat

    # 20250208
    # https://leetcode.cn/problems/unique-paths-ii/description/?envType=daily-question&envId=2025-02-08
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        @cache  # 缓存装饰器，避免重复计算 dfs 的结果（一行代码实现记忆化）
        def dfs(i: int, j: int) -> int:
            if i < 0 or j < 0 or obstacleGrid[i][j]:
                return 0
            if i == 0 and j == 0:
                return 1
            return dfs(i - 1, j) + dfs(i, j - 1)

        m, n = len(obstacleGrid), len(obstacleGrid[0])
        return dfs(m - 1, n - 1)

    # 20250209
    # https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/description/?envType=daily-question&envId=2025-02-09
    def removeDuplicates(self, nums: List[int]) -> int:
        stack_size = 2  # 栈的大小，前两个元素默认保留
        for i in range(2, len(nums)):
            if nums[i] != nums[stack_size - 2]:  # 和栈顶下方的元素比较
                nums[stack_size] = nums[i]  # 入栈
                stack_size += 1
        return min(stack_size, len(nums))
    def catMouseGame(self, graph):
        N = len(graph)

        # What nodes could play their turn to
        # arrive at node (m, c, t) ?
        def parents(m, c, t):
            if t == 2:
                for m2 in graph[m]:
                    yield m2, c, 3-t
            else:
                for c2 in graph[c]:
                    if c2:
                        yield m, c2, 3-t

        DRAW, MOUSE, CAT = 0, 1, 2
        color = collections.defaultdict(int)

        # degree[node] : the number of neutral children of this node
        degree = {}
        for m in range(N):
            for c in range(N):
                degree[m,c,1] = len(graph[m])
                degree[m,c,2] = len(graph[c]) - (0 in graph[c])

        # enqueued : all nodes that are colored
        queue = collections.deque([])
        for i in range(N):
            for t in range(1, 3):
                color[0, i, t] = MOUSE
                queue.append((0, i, t, MOUSE))
                if i > 0:
                    color[i, i, t] = CAT
                    queue.append((i, i, t, CAT))

        # percolate
        while queue:
            # for nodes that are colored :
            i, j, t, c = queue.popleft()
            # for every parent of this node i, j, t :
            for i2, j2, t2 in parents(i, j, t):
                # if this parent is not colored :
                if color[i2, j2, t2] is DRAW:
                    # if the parent can make a winning move (ie. mouse to MOUSE), do so
                    if t2 == c: # winning move
                        color[i2, j2, t2] = c
                        queue.append((i2, j2, t2, c))
                    # else, this parent has degree[parent]--, and enqueue if all children
                    # of this parent are colored as losing moves
                    else:
                        degree[i2, j2, t2] -= 1
                        if degree[i2, j2, t2] == 0:
                            color[i2, j2, t2] = 3 - t2
                            queue.append((i2, j2, t2, 3 - t2))

        return color[1, 2, 1]

    # 20250211
    # https://leetcode.cn/problems/cat-and-mouse-ii/description/?envType=daily-question&envId=2025-02-11
    def catMouseGame(self, g_mouse: List[List[int]], g_cat: List[List[int]], mouse_start: int, cat_start: int, hole: int) -> int:
        n = len(g_mouse)
        deg = [[[0, 0] for _ in range(n)] for _ in range(n)]
        for i in range(n):
            for j in range(n):
                deg[i][j][0] = len(g_mouse[i])
                deg[i][j][1] = len(g_cat[j])

        winner = [[[0, 0] for _ in range(n)] for _ in range(n)]
        q = deque()
        for i in range(n):
            winner[hole][i][1] = 1  # 鼠到达洞中（此时轮到猫移动），鼠获胜
            winner[i][hole][0] = 2  # 猫到达洞中（此时轮到鼠移动），猫获胜
            winner[i][i][0] = winner[i][i][1] = 2  # 猫和鼠出现在同一个节点，无论轮到谁移动，都是猫获胜
            q.append((hole, i, 1))
            q.append((i, hole, 0))
            q.append((i, i, 0))
            q.append((i, i, 1))

        # 获取 (mouse, cat, turn) 的上个状态（值尚未确定）
        def get_pre_states() -> List[Tuple[int, int]]:
            if turn:  # 当前轮到猫移动，枚举上一轮鼠的位置
                return [(pre_mouse, cat) for pre_mouse in g_mouse[mouse] if winner[pre_mouse][cat][0] == 0]
            # 当前轮到鼠移动，枚举上一轮猫的位置
            return [(mouse, pre_cat) for pre_cat in g_cat[cat] if winner[mouse][pre_cat][1] == 0]

        # 减少上个状态的度数
        def dec_deg_to_zero() -> bool:
            deg[pre_mouse][pre_cat][pre_turn] -= 1
            return deg[pre_mouse][pre_cat][pre_turn] == 0

        while q:
            mouse, cat, turn = q.popleft()
            win = winner[mouse][cat][turn]  # 最终谁赢了
            pre_turn = turn ^ 1
            for pre_mouse, pre_cat in get_pre_states():
                # 情况一：如果上一回合鼠从 pre 移动到 cur，最终鼠赢，那么标记 pre 状态的 winner = 鼠
                # 情况二：如果上一回合猫从 pre 移动到 cur，最终猫赢，那么标记 pre 状态的 winner = 猫
                # 情况三：如果上一回合鼠从 pre 移动到 cur，最终猫赢，那么待定，直到我们发现从 pre 出发能到达的状态都是猫赢，那么标记 pre 状态的 winner = 猫
                # 情况四：如果上一回合猫从 pre 移动到 cur，最终鼠赢，那么待定，直到我们发现从 pre 出发能到达的状态都是鼠赢，那么标记 pre 状态的 winner = 鼠
                if pre_turn == win - 1 or dec_deg_to_zero():
                    winner[pre_mouse][pre_cat][pre_turn] = win
                    q.append((pre_mouse, pre_cat, pre_turn))

        # 鼠在节点 mouse_start，猫在节点 cat_start，当前轮到鼠移动
        return winner[mouse_start][cat_start][0]  # 返回最终谁赢了（或者平局）

    def canMouseWin(self, grid: List[str], catJump: int, mouseJump: int) -> bool:
        DIRS = (0, -1), (0, 1), (-1, 0), (1, 0)  # 左右上下
        m, n = len(grid), len(grid[0])
        # 鼠和猫分别建图
        g_mouse = [[] for _ in range(m * n)]
        g_cat = [[] for _ in range(m * n)]
        for i, row in enumerate(grid):
            for j, c in enumerate(row):
                if c == '#':  # 墙
                    continue
                if c == 'M':  # 鼠的位置
                    mx, my = i, j
                elif c == 'C':  # 猫的位置
                    cx, cy = i, j
                elif c == 'F':  # 食物（洞）的位置
                    fx, fy = i, j
                v = i * n + j  # 二维坐标 (i,j) 映射为一维坐标 v
                for dx, dy in DIRS:  # 枚举左右上下四个方向
                    for k in range(mouseJump + 1):  # 枚举跳跃长度
                        x, y = i + k * dx, j + k * dy
                        if not (0 <= x < m and 0 <= y < n and grid[x][y] != '#'):  # 出界或者遇到墙
                            break
                        g_mouse[v].append(x * n + y)  # 连边
                    for k in range(catJump + 1):  # 枚举跳跃长度
                        x, y = i + k * dx, j + k * dy
                        if not (0 <= x < m and 0 <= y < n and grid[x][y] != '#'):  # 出界或者遇到墙
                            break
                        g_cat[v].append(x * n + y)  # 连边

        # 判断是否鼠赢
        return self.catMouseGame(g_mouse, g_cat, mx * n + my, cx * n + cy, fx * n + fy) == 1

# 202212230
# https://leetcode.cn/problems/exam-room/
# https://leetcode.cn/problems/exam-room/solution/by-meat-puppets-v6es/
class ExamRoom:

    def __init__(self, n: int):
        self.students = []
        self.n = n

    def seat(self) -> int:
        if not self.students:
            student = 0
        else:
            dist = self.students[0]
            student = 0
            
            for i, s in enumerate(self.students):
                prev = self.students[i-1] 
                d = (s-prev)//2
                if d > dist:
                    dist = d
                    student = prev + d
                d = self.n - 1 - self.students[-1]
            if d > dist:
                student = self.n -1
        bisect.insort(self.students,student)
        return student

    def leave(self, p: int) -> None:
        if p in self.students:
            self.students.remove(p)
        return

# 20230118
# https://leetcode.cn/problems/finding-mk-average/
# https://leetcode.cn/problems/finding-mk-average/solution/by-lcbin-bonb/
class MKAverage:

    def __init__(self, m: int, k: int):
        self.m = m
        self.k = k
        self.s = 0
        self.q = deque()
        self.lo = SortedList()
        self.mid = SortedList()
        self.hi = SortedList()

    def addElement(self, num: int) -> None:
        if not self.lo or num <= self.lo[-1]:
            self.lo.add(num)
        elif not self.hi or num >= self.hi[0]:
            self.hi.add(num)
        else:
            self.mid.add(num)
            self.s += num
        self.q.append(num)
        if len(self.q) > self.m:
            x = self.q.popleft()
            if x in self.lo:
                self.lo.remove(x)
            elif x in self.hi:
                self.hi.remove(x)
            else:
                self.mid.remove(x)
                self.s -= x
        while len(self.lo) > self.k:
            x = self.lo.pop()
            self.mid.add(x)
            self.s += x
        while len(self.hi) > self.k:
            x = self.hi.pop(0)
            self.mid.add(x)
            self.s += x
        while len(self.lo) < self.k and self.mid:
            x = self.mid.pop(0)
            self.lo.add(x)
            self.s -= x
        while len(self.hi) < self.k and self.mid:
            x = self.mid.pop()
            self.hi.add(x)
            self.s -= x

    def calculateMKAverage(self) -> int:
        return -1 if len(self.q) < self.m else self.s // (self.m - 2 * self.k)

# 20230209
# https://leetcode.cn/problems/design-authentication-manager/
class AuthenticationManager:

    def __init__(self, timeToLive: int):
        self.db = {}
        self.timeToLive = timeToLive

    def generate(self, tokenId: str, currentTime: int) -> None:
        self.db[tokenId] = currentTime+self.timeToLive

    def renew(self, tokenId: str, currentTime: int) -> None:
        if tokenId in self.db.keys() and self.db[tokenId] > currentTime:
            self.db[tokenId] = currentTime+self.timeToLive

    def countUnexpiredTokens(self, currentTime: int) -> int:
        res = 0
        for key in self.db:
            if self.db[key] > currentTime:
                res += 1
        return res

# 20230324
# https://leetcode.cn/problems/stream-of-characters/
# https://leetcode.cn/problems/stream-of-characters/solution/ji-ben-de-zi-fu-chuan-cao-zuo-1032-zi-fu-gyq8/
class StreamChecker:

    def __init__(self, words: List[str]):
        self.s = ""
        self.words = words

    def query(self, letter: str) -> bool:
        self.s += letter
        return any(self.s.endswith(x) for x in self.words)

# 20230416
# https://leetcode.cn/problems/online-majority-element-in-subarray/
# https://leetcode.cn/problems/online-majority-element-in-subarray/solution/fen-xi-yi-xia-by-v5qyy4q65w-6lcx/
class MajorityChecker:
    
    def __init__(self, arr: List[int]):

        self.t = defaultdict(list)
        for j in range(len(arr)):
            self.t[arr[j]].append(j) #按顺序遍历数组，每个元素的下标数组就自然是有序，可以直接二分
        self.arr = arr

    def query(self, left: int, right: int, threshold: int) -> int:

        for _ in range(25): # 随机采样25次
            num = self.arr[random.randint(left,right)] # 在指定区间里抽取
            rgt = bisect_right(self.t[num],right) # 找严格大于right的，这样不管right本身在不在下标数组内，总数都是对的
            lft = bisect_left(self.t[num],left)
            if rgt-lft>=threshold: # 数量足够就返回
                return num
        return -1 # 25次找不到就默认不存在，对于单组case的出错概率低于0.03%

# 20230428
# https://leetcode.cn/problems/dinner-plate-stacks/
# https://leetcode.cn/problems/dinner-plate-stacks/solution/yu-qi-wei-hu-di-yi-ge-wei-man-zhan-bu-ru-sphs/
class DinnerPlates:
    def __init__(self, capacity: int):
        self.capacity = capacity  # 栈的容量
        self.stacks = []  # 所有栈
        self.h = []  # 最小堆，保存未满栈的下标

    def push(self, val: int) -> None:
        if self.h and self.h[0] >= len(self.stacks):
            self.h = []  # 堆中都是越界下标，直接清空
        if self.h:  # 还有未满栈
            self.stacks[self.h[0]].append(val)  # 入栈
            if len(self.stacks[self.h[0]]) == self.capacity:  # 栈满了
                heappop(self.h)  # 从堆中去掉
        else:  # 所有栈都是满的
            self.stacks.append([val])  # 添加一个新的栈
            if self.capacity > 1:  # 新的栈没有满
                heappush(self.h, len(self.stacks) - 1)  # 入堆

    def pop(self) -> int:
        # 等价为 popAtStack 最后一个非空栈
        return self.popAtStack(len(self.stacks) - 1)

    def popAtStack(self, index: int) -> int:
        if index < 0 or index >= len(self.stacks) or len(self.stacks[index]) == 0:
            return -1  # 非法操作
        if len(self.stacks[index]) == self.capacity:  # 满栈
            heappush(self.h, index)  # 元素出栈后，栈就不满了，把下标入堆
        val = self.stacks[index].pop()
        while self.stacks and len(self.stacks[-1]) == 0:
            self.stacks.pop()  # 去掉末尾的空栈（懒删除，堆中下标在 push 时处理）
        return val

# 20230612
# https://leetcode.cn/problems/kth-ancestor-of-a-tree-node/
# https://leetcode.cn/problems/kth-ancestor-of-a-tree-node/solution/mo-ban-jiang-jie-shu-shang-bei-zeng-suan-v3rw/
class TreeAncestor:
    def __init__(self, n: int, parent: List[int]):
        m = n.bit_length() - 1
        pa = [[p] + [-1] * m for p in parent]
        for i in range(m):
            for x in range(n):
                if (p := pa[x][i]) != -1:
                    pa[x][i + 1] = pa[p][i]
        self.pa = pa

    def getKthAncestor(self, node: int, k: int) -> int:
        for i in range(k.bit_length()):
            if (k >> i) & 1:  # k 的二进制从低到高第 i 位是 1
                node = self.pa[node][i]
                if node < 0: break
        return node

    # 另一种写法，不断去掉 k 的最低位的 1
    def getKthAncestor2(self, node: int, k: int) -> int:
        while k and node != -1:  # 也可以写成 ~node
            lb = k & -k
            node = self.pa[node][lb.bit_length() - 1]
            k ^= lb
        return node        

# 20230904
# https://leetcode.cn/problems/serialize-and-deserialize-bst/description/?envType=daily-question&envId=2023-09-04
# https://leetcode.cn/problems/serialize-and-deserialize-bst/solutions/2425176/python3goqian-xu-bian-li-di-gui-by-fdvoi-y89a/?envType=daily-question&envId=2023-09-04
class Codec:

    def serialize(self, root: Optional[TreeNode]) -> str:
        """Encodes a tree to a single string.
        """
        if not root:
            return ""
        
        def serialize_helper(node):
            if not node:
                return "None,"
            
            # 将节点值转换为字符串并添加到结果中
            result = str(node.val) + ","
            
            # 递归序列化左子树和右子树
            left = serialize_helper(node.left)
            right = serialize_helper(node.right)
            
            # 合并左子树、右子树和节点值
            result += left + right
            
            return result
        
        return serialize_helper(root)

    def deserialize(self, data: str) -> Optional[TreeNode]:
        """Decodes your encoded data to tree.
        """
        if not data:
            return None
        
        def deserialize_helper(values):
            # 从序列化字符串中获取下一个节点的值
            value = values.pop(0)
            
            if value == "None":
                return None
            
            # 创建一个节点并设置其值
            node = TreeNode(int(value))
            
            # 递归反序列化左子树和右子树
            node.left = deserialize_helper(values)
            node.right = deserialize_helper(values)
            
            return node
        
        # 将序列化字符串拆分为节点值的列表
        values = data.split(",")
        
        # 从根节点开始反序列化
        return deserialize_helper(values)

# 20230923
# https://leetcode.cn/problems/operations-on-tree/description/?envType=daily-question&envId=2023-09-23
class LockingTree:

    def __init__(self, parent: List[int]):
        self.parent = parent
        self.n = len(parent)
        self.grand = [-1] * self.n
        self.son = [[] for _ in range(self.n)]
        for i, x in enumerate(parent[1:], start=1):
            self.grand[i] = x
            self.son[x].append(i)
        self.state = [(-1, False) for _ in range(self.n)]
        self.Lock = False

    def lock(self, num: int, user: int) -> bool:
        gent, lock = self.state[num]
        if not lock:
            self.state[num] = (user, True)
            self.Lock = True
            return True
        else:
            return False
            
    def unlock(self, num: int, user: int) -> bool:
        gent, lock = self.state[num]
        if gent == user:
            self.state[num] = (-1, False)
            return True
        else:
            return False

    def upgrade(self, num: int, user: int) -> bool:
        if not self.Lock:return False
        gent, lock = self.state[num]
        if not lock and self.check_son_lock(num) and self.check_grand_unlock(num):
            self.state[num] = (user, True)
            self.unlock_son(num)
            return True
        else:
            return False

    def check_son_lock(self, num):
        q = self.son[num].copy()
        while q:
            node = q.pop()
            if self.state[node][1]:
                return True
            q.extend(self.son[node])
        return False

    def check_grand_unlock(self, num):
        if num == 0: return True
        q = [self.grand[num]]
        while q:
            node = q.pop()
            if self.state[node][1]:
                return False
            if self.grand[node] > -1:
                q.append(self.grand[node])
        return True

    def unlock_son(self, num):
        q = self.son[num].copy()
        while q:
            node = q.pop()
            self.state[node] = (-1, False)
            q.extend(self.son[node])

# 20230924
# https://leetcode.cn/problems/lru-cache/description/?envType=daily-question&envId=2023-09-24
class ListNode:
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.hashmap = {}
        # 新建两个节点 head 和 tail
        self.head = ListNode()
        self.tail = ListNode()
        # 初始化链表为 head <-> tail
        self.head.next = self.tail
        self.tail.prev = self.head

    # 因为get与put操作都可能需要将双向链表中的某个节点移到末尾，所以定义一个方法
    def move_node_to_tail(self, key):
            # 先将哈希表key指向的节点拎出来，为了简洁起名node
            #      hashmap[key]                               hashmap[key]
            #           |                                          |
            #           V              -->                         V
            # prev <-> node <-> next         pre <-> next   ...   node
            node = self.hashmap[key]
            node.prev.next = node.next
            node.next.prev = node.prev
            # 之后将node插入到尾节点前
            #                 hashmap[key]                 hashmap[key]
            #                      |                            |
            #                      V        -->                 V
            # prev <-> tail  ...  node                prev <-> node <-> tail
            node.prev = self.tail.prev
            node.next = self.tail
            self.tail.prev.next = node
            self.tail.prev = node

    def get(self, key: int) -> int:
        if key in self.hashmap:
            # 如果已经在链表中了久把它移到末尾（变成最新访问的）
            self.move_node_to_tail(key)
        res = self.hashmap.get(key, -1)
        if res == -1:
            return res
        else:
            return res.value

    def put(self, key: int, value: int) -> None:
        if key in self.hashmap:
            # 如果key本身已经在哈希表中了就不需要在链表中加入新的节点
            # 但是需要更新字典该值对应节点的value
            self.hashmap[key].value = value
            # 之后将该节点移到末尾
            self.move_node_to_tail(key)
        else:
            if len(self.hashmap) == self.capacity:
                # 去掉哈希表对应项
                self.hashmap.pop(self.head.next.key)
                # 去掉最久没有被访问过的节点，即头节点之后的节点
                self.head.next = self.head.next.next
                self.head.next.prev = self.head
            # 如果不在的话就插入到尾节点前
            new = ListNode(key, value)
            self.hashmap[key] = new
            new.prev = self.tail.prev
            new.next = self.tail
            self.tail.prev.next = new
            self.tail.prev = new

# 20230925
# https://leetcode.cn/problems/lfu-cache/description/?envType=daily-question&envId=2023-09-25
class Node:
    # 提高访问属性的速度，并节省内存
    __slots__ = 'prev', 'next', 'key', 'value', 'freq'

    def __init__(self, key=0, val=0):
        self.key = key
        self.value = val
        self.freq = 1  #  新书只读了一次

class LFUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.key_to_node = dict()
        def new_list() -> Node:
            dummy = Node()  # 哨兵节点
            dummy.prev = dummy
            dummy.next = dummy
            return dummy
        self.freq_to_dummy = defaultdict(new_list)

    def get_node(self, key: int) -> Optional[Node]:
        if key not in self.key_to_node:  # 没有这本书
            return None
        node = self.key_to_node[key]  # 有这本书
        self.remove(node)  # 把这本书抽出来
        dummy = self.freq_to_dummy[node.freq]
        if dummy.prev == dummy:  # 抽出来后，这摞书是空的
            del self.freq_to_dummy[node.freq]  # 移除空链表
            if self.min_freq == node.freq:
                self.min_freq += 1
        node.freq += 1
        self.push_front(self.freq_to_dummy[node.freq], node)  # 放在右边这摞书的最上面
        return node

    def get(self, key: int) -> int:
        node = self.get_node(key)
        return node.value if node else -1

    def put(self, key: int, value: int) -> None:
        node = self.get_node(key)
        if node:  # 有这本书
            node.value = value  # 更新 value
            return
        if len(self.key_to_node) == self.capacity:  # 书太多了
            dummy = self.freq_to_dummy[self.min_freq]
            back_node = dummy.prev  # 最左边那摞书的最下面的书
            del self.key_to_node[back_node.key]
            self.remove(back_node)  # 移除
            if dummy.prev == dummy:  # 这摞书是空的
                del self.freq_to_dummy[self.min_freq]  # 移除空链表
        self.key_to_node[key] = node = Node(key, value)  # 新书
        self.push_front(self.freq_to_dummy[1], node)  # 放在「看过 1 次」的最上面
        self.min_freq = 1

    # 删除一个节点（抽出一本书）
    def remove(self, x: Node) -> None:
        x.prev.next = x.next
        x.next.prev = x.prev

    # 在链表头添加一个节点（把一本书放在最上面）
    def push_front(self, dummy: Node, x: Node) -> None:
        x.prev = dummy
        x.next = dummy.next
        x.prev.next = x
        x.next.prev = x

# 20231007
# https://leetcode.cn/problems/online-stock-span/description/?envType=daily-question&envId=2023-10-07
class StockSpanner:
    def __init__(self):
        self.stack = [(-1, inf)]  # 这样无需判断栈为空的情况
        self.cur_day = -1  # 第一个 next 调用算作第 0 天

    def next(self, price: int) -> int:
        while price >= self.stack[-1][1]:
            self.stack.pop()  # 栈顶数据后面不会再用到了，因为 price 更大
        self.cur_day += 1
        self.stack.append((self.cur_day, price))
        return self.cur_day - self.stack[-2][0]

# 20231008
# https://leetcode.cn/problems/stock-price-fluctuation/description/?envType=daily-question&envId=2023-10-08
class StockPrice:
    def __init__(self):
        self.priceList = dict()
        self.latest_time = -1
        self.maxHeap = []
        self.minHeap = []

    def update(self, timestamp: int, price: int) -> None:
        self.priceList[timestamp] = price
        self.latest_time = max(self.latest_time, timestamp)
        heapq.heappush(self.maxHeap, (-price, timestamp))
        heapq.heappush(self.minHeap, (price, timestamp))

    def current(self) -> int:
        return self.priceList[self.latest_time]

    def maximum(self) -> int:
        while -self.maxHeap[0][0] != self.priceList[self.maxHeap[0][1]]:
            heapq.heappop(self.maxHeap)
        return -self.maxHeap[0][0]

    def minimum(self) -> int:
        while self.minHeap[0][0] != self.priceList[self.minHeap[0][1]]:
            heapq.heappop(self.minHeap)
        return self.minHeap[0][0]

# 20231112
# https://leetcode.cn/problems/range-module/description/?envType=daily-question&envId=2023-11-12
class Node:
    __slots__ = ['left', 'right', 'add', 'v']

    def __init__(self):
        self.left = None
        self.right = None
        self.add = 0
        self.v = False

class SegmentTree:
    __slots__ = ['root']

    def __init__(self):
        self.root = Node()

    def modify(self, left, right, v, l=1, r=int(1e9), node=None):
        if node is None:
            node = self.root
        if l >= left and r <= right:
            if v == 1:
                node.add = 1
                node.v = True
            else:
                node.add = -1
                node.v = False
            return
        self.pushdown(node)
        mid = (l + r) >> 1
        if left <= mid:
            self.modify(left, right, v, l, mid, node.left)
        if right > mid:
            self.modify(left, right, v, mid + 1, r, node.right)
        self.pushup(node)

    def query(self, left, right, l=1, r=int(1e9), node=None):
        if node is None:
            node = self.root
        if l >= left and r <= right:
            return node.v
        self.pushdown(node)
        mid = (l + r) >> 1
        v = True
        if left <= mid:
            v = v and self.query(left, right, l, mid, node.left)
        if right > mid:
            v = v and self.query(left, right, mid + 1, r, node.right)
        return v

    def pushup(self, node):
        node.v = bool(node.left and node.left.v and node.right and node.right.v)

    def pushdown(self, node):
        if node.left is None:
            node.left = Node()
        if node.right is None:
            node.right = Node()
        if node.add:
            node.left.add = node.right.add = node.add
            node.left.v = node.add == 1
            node.right.v = node.add == 1
            node.add = 0

class RangeModule:
    def __init__(self):
        self.tree = SegmentTree()

    def addRange(self, left: int, right: int) -> None:
        self.tree.modify(left, right - 1, 1)

    def queryRange(self, left: int, right: int) -> bool:
        return self.tree.query(left, right - 1)

    def removeRange(self, left: int, right: int) -> None:
        self.tree.modify(left, right - 1, -1)

# 20231113
# https://leetcode.cn/problems/range-sum-query-mutable/description/?envType=daily-question&envId=2023-11-13
class NumArray:
    __slots__ = 'nums', 'tree'

    def __init__(self, nums: List[int]):
        n = len(nums)
        self.nums = [0] * n  # 使 update 中算出的 delta = nums[i]
        self.tree = [0] * (n + 1)
        for i, x in enumerate(nums):
            self.update(i, x)

    def update(self, index: int, val: int) -> None:
        delta = val - self.nums[index]
        self.nums[index] = val
        i = index + 1
        while i < len(self.tree):
            self.tree[i] += delta
            i += i & -i

    def prefixSum(self, i: int) -> int:
        s = 0
        while i:
            s += self.tree[i]
            i &= i - 1  # i -= i & -i 的另一种写法
        return s

    def sumRange(self, left: int, right: int) -> int:
        return self.prefixSum(right + 1) - self.prefixSum(left)

# 20231128
# https://leetcode.cn/problems/design-front-middle-back-queue/description/?envType=daily-question&envId=2023-11-28
class FrontMiddleBackQueue:

    def __init__(self):
        self.front_half = deque()   # 存储前半部分元素的队列
        self.back_half = deque()     # 存储后半部分元素的队列

    def pushFront(self, val: int) -> None:
        self.front_half.appendleft(val)     # 前队列队首加入一个元素
        self.check_and_front2back()         # 平衡两个队列元素（前队列元素变多，前往后移）


    def pushMiddle(self, val: int) -> None:
        if(len(self.front_half) > len(self.back_half)):
            # 前队列比后队列多一个元素，先将这个元素移到后队列，再添加元素
            self.back_half.appendleft(self.front_half.pop())
        
        self.front_half.append(val)   # 前队列队尾位置始终为中间元素


    def pushBack(self, val: int) -> None:
        self.back_half.append(val)      # 后队列队尾
        self.check_and_back2front()     # 平衡两个队列元素（后队列元素变多，后往前移）


    def popFront(self) -> int:
        if not self.front_half: return -1     # 前队列都为空，说明整个队列都为空，直接返回-1
        val = self.front_half.popleft()       # 获取前队列队首元素
        self.check_and_back2front()   # 平衡两个队列元素（前队列元素减少相当于后队列元素变多，后往前移）
        return val


    def popMiddle(self) -> int:
        if not self.front_half: return -1    # 前队列都为空，说明整个队列都为空，直接返回-1
        val = self.front_half.pop()          # 前队列队尾位置始终为中间元素
        self.check_and_back2front()   # 平衡两个队列元素（前队列元素减少相当于后队列元素变多，后往前移）
        return val


    def popBack(self) -> int:
        if not self.back_half:
            if not self.front_half: return -1  # 前队列都为空，说明整个队列都为空，直接返回-1
            return self.front_half.pop()        # 后队列为空，前队列不为空，前队列只能有一个元素
        val = self.back_half.pop()    # 获取后队列队尾元素
        self.check_and_front2back()   # 平衡两个队列元素（后队列元素减少相当于前队列元素变多，前往后移）
        return val


    """
    校验前队列元素是否多于后队列元素一个，是的话将前队列队尾元素移动到后队列队首
    """
    def check_and_front2back(self):
        if len(self.front_half) > len(self.back_half) + 1:
            self.back_half.appendleft(self.front_half.pop())  # 后队列队首添加前队列队尾元素，并移除前队列队尾元素

    """
    校验后队列元素是否多于前队列元素，是的话将后队列队首元素移动到后队列队尾
    """
    def check_and_back2front(self):
        if len(self.back_half) > len(self.front_half):
            self.front_half.append(self.back_half.popleft())   # 前队列队尾添加后队列队首元素，并移除后队列队首元素

# 20231129
# https://leetcode.cn/problems/smallest-number-in-infinite-set/description/?envType=daily-question&envId=2023-11-29
class SmallestInfiniteSet:
    def __init__(self):
        self.vis = [False] * 1010
        self.q = []
        self.idx = 1

    def popSmallest(self):
        ans = -1
        if self.q:
            ans = heapq.heappop(self.q)
            self.vis[ans] = False
        else:
            ans = self.idx
            self.idx += 1
        return ans

    def addBack(self, x):
        if x >= self.idx or self.vis[x]:
            return
        if x == self.idx - 1:
            self.idx -= 1
        else:
            heapq.heappush(self.q, x)
            self.vis[x] = True

# 20231216
# https://leetcode.cn/problems/count-integers-in-intervals/description/?envType=daily-question&envId=2023-12-16
class Node:
    __slots__ = ("left", "right", "l", "r", "mid", "v", "add")

    def __init__(self, l, r):
        self.left = None
        self.right = None
        self.l = l
        self.r = r
        self.mid = (l + r) // 2
        self.v = 0
        self.add = 0


class SegmentTree:
    def __init__(self):
        self.root = Node(1, int(1e9) + 1)

    def modify(self, l, r, v, node=None):
        if node is None:
            node = self.root
        if l > r:
            return
        if node.l >= l and node.r <= r:
            node.v = node.r - node.l + 1
            node.add = v
            return
        self.pushdown(node)
        if l <= node.mid:
            self.modify(l, r, v, node.left)
        if r > node.mid:
            self.modify(l, r, v, node.right)
        self.pushup(node)

    def query(self, l, r, node=None):
        if node is None:
            node = self.root
        if l > r:
            return 0
        if node.l >= l and node.r <= r:
            return node.v
        self.pushdown(node)
        v = 0
        if l <= node.mid:
            v += self.query(l, r, node.left)
        if r > node.mid:
            v += self.query(l, r, node.right)
        return v

    def pushup(self, node):
        node.v = node.left.v + node.right.v

    def pushdown(self, node):
        if node.left is None:
            node.left = Node(node.l, node.mid)
        if node.right is None:
            node.right = Node(node.mid + 1, node.r)
        if node.add != 0:
            left, right = node.left, node.right
            left.add = node.add
            right.add = node.add
            left.v = left.r - left.l + 1
            right.v = right.r - right.l + 1
            node.add = 0

class CountIntervals:
    def __init__(self):
        self.tree = SegmentTree()

    def add(self, left, right):
        self.tree.modify(left, right, 1)

    def count(self):
        return self.tree.query(1, int(1e9))

# 20240303
# https://leetcode.cn/problems/implement-stack-using-queues/description/?envType=daily-question&envId=2024-03-03
class MyStack:
    def __init__(self):
        self.q1 = deque()
        self.q2 = deque()

    def push(self, x: int) -> None:
        self.q2.append(x)
        while self.q1:
            self.q2.append(self.q1.popleft())
        self.q1, self.q2 = self.q2, self.q1

    def pop(self) -> int:
        return self.q1.popleft()

    def top(self) -> int:
        return self.q1[0]

    def empty(self) -> bool:
        return len(self.q1) == 0

# 20240304
# https://leetcode.cn/problems/implement-queue-using-stacks/description/?envType=daily-question&envId=2024-03-04
class MyQueue:

    def __init__(self):
        self.A, self.B = [], []

    def push(self, x: int) -> None:
        self.A.append(x)

    def pop(self) -> int:
        peek = self.peek()
        self.B.pop()
        return peek

    def peek(self) -> int:
        if self.B: return self.B[-1]
        if not self.A: return -1
        # 将栈 A 的元素依次移动至栈 B
        while self.A:
            self.B.append(self.A.pop())
        return self.B[-1]

    def empty(self) -> bool:
        return not self.A and not self.B

# 20240312
# https://leetcode.cn/problems/find-elements-in-a-contaminated-binary-tree/description/?envType=daily-question&envId=2024-03-12
class FindElements:
    def __init__(self, root: Optional[TreeNode]):
        s = set()
        def dfs(node: Optional[TreeNode], val: int) -> None:
            if node is None:
                return
            s.add(val)
            dfs(node.left, val * 2 + 1)
            dfs(node.right, val * 2 + 2)
        dfs(root, 0)
        self.s = s

    def find(self, target: int) -> bool:
        return target in self.s

# 20240318
# https://leetcode.cn/problems/range-sum-query-immutable/description/?envType=daily-question&envId=2024-03-18
class NumArray:
    def __init__(self, nums: List[int]):
        # s = list(accumulate(nums, initial=0))
        s = [0] * (len(nums) + 1)
        for i, x in enumerate(nums):
            s[i + 1] = s[i] + x
        self.s = s

    def sumRange(self, left: int, right: int) -> int:
        return self.s[right + 1] - self.s[left]

# 20240321
# https://leetcode.cn/problems/frequency-tracker/description/?envType=daily-question&envId=2024-03-21
class FrequencyTracker:
    def __init__(self):
        self.cnt = Counter()  # number 的出现次数
        self.freq = Counter()  # number 的出现次数的出现次数

    def add(self, number: int, delta=1) -> None:
        self.freq[self.cnt[number]] -= 1  # 去掉一个旧的 cnt[number]
        self.cnt[number] += delta
        self.freq[self.cnt[number]] += 1  # 添加一个新的 cnt[number]

    def deleteOne(self, number: int) -> None:
        if self.cnt[number]:
            self.add(number, -1)

    def hasFrequency(self, frequency: int) -> bool:
        return self.freq[frequency] > 0  # 至少有一个 number 的出现次数恰好为 frequency

# 20240326
# https://leetcode.cn/problems/design-graph-with-shortest-path-calculator/description/?envType=daily-question&envId=2024-03-26
class Graph:
    def __init__(self, n: int, edges: List[List[int]]):
        self.g = [[inf] * n for _ in range(n)]  # 邻接矩阵
        for x, y, w in edges:
            self.g[x][y] = w  # 添加一条边（题目保证没有重边）

    def addEdge(self, e: List[int]) -> None:
        self.g[e[0]][e[1]] = e[2]  # 添加一条边（题目保证这条边之前不存在）

    def shortestPath(self, start: int, end: int) -> int:
        n = len(self.g)
        dis = [inf] * n  # 从 start 出发，到各个点的最短路，如果不存在则为无穷大
        dis[start] = 0
        vis = [False] * n
        while True:  # 至多循环 n 次
            x = -1
            for i, (b, d) in enumerate(zip(vis, dis)):
                if not b and (x < 0 or d < dis[x]):
                    x = i
            if x < 0 or dis[x] == inf:  # 所有从 start 能到达的点都被更新了
                return -1  # 无法到达终点
            if x == end:  # 找到终点，提前退出
                return dis[x]
            vis[x] = True  # 标记，在后续的循环中无需反复更新 x 到其余点的最短路长度
            for y, w in enumerate(self.g[x]):
                if dis[x] + w < dis[y]:
                    dis[y] = dis[x] + w  # 更新最短路长度

# 20240406
# https://leetcode.cn/problems/kth-ancestor-of-a-tree-node/description/?envType=daily-question&envId=2024-04-06
class TreeAncestor:
    def __init__(self, n: int, parent: List[int]):
        m = n.bit_length() - 1
        pa = [[p] + [-1] * m for p in parent]
        for i in range(m):
            for x in range(n):
                if (p := pa[x][i]) != -1:
                    pa[x][i + 1] = pa[p][i]
        self.pa = pa

    def getKthAncestor(self, node: int, k: int) -> int:
        for i in range(k.bit_length()):
            if (k >> i) & 1:  # k 的二进制从低到高第 i 位是 1
                node = self.pa[node][i]
                if node < 0: break
        return node

    # 另一种写法，不断去掉 k 的最低位的 1
    def getKthAncestor2(self, node: int, k: int) -> int:
        while k and node != -1:  # 也可以写成 ~node
            lb = k & -k
            node = self.pa[node][lb.bit_length() - 1]
            k ^= lb
        return node

# 20240407
# https://leetcode.cn/problems/throne-inheritance/description/?envType=daily-question&envId=2024-04-07
class ThroneInheritance:

    def __init__(self, kingName: str):
        self.childrens = {kingName: []}     # 存储每个人的孩子列表
        self.has_dead = set()   # 记录已经死亡的人
        self.king = kingName    # 记录国王名字，搜索起点

    def birth(self, parentName: str, childName: str) -> None:
        self.childrens[parentName].append(childName)    # 添加父子关系
        self.childrens[childName] = []                  # 初始化孩子的孩子列表

    def death(self, name: str) -> None:
        self.has_dead.add(name)     # 记录死亡的人

    def getInheritanceOrder(self) -> List[str]:
        """
        递归搜索继承顺序
        """
        def dfs(people: str):
            if people not in self.has_dead:
                res.append(people)  # people还活着，则添加到继承表里
            for child in self.childrens[people]:
                dfs(child)      # 枚举其每一个孩子递归搜索

        res = []
        dfs(self.king)  # 从祖先节点开始搜索
        return res

# 20240414
# https://leetcode.cn/problems/design-hashset/description/?envType=daily-question&envId=2024-04-14
class MyHashSet:
    def __init__(self):
        self.data = [False] * 1000001

    def add(self, key: int) -> None:
        self.data[key] = True

    def remove(self, key: int) -> None:
        self.data[key] = False

    def contains(self, key: int) -> bool:
        return self.data[key]

# 20240415
# https://leetcode.cn/problems/design-hashmap/description/?envType=daily-question&envId=2024-04-15
class MyHashMap:
    def __init__(self):
        self.data = [-1] * 1000001

    def put(self, key: int, value: int) -> None:
        self.data[key] = value

    def get(self, key: int) -> int:
        return self.data[key]

    def remove(self, key: int) -> None:
        self.data[key] = -1

# 20240426
# https://leetcode.cn/problems/snapshot-array/description/?envType=daily-question&envId=2024-04-26
class SnapshotArray:
    def __init__(self, _: int):
        self.cur_snap_id = 0
        self.history = defaultdict(list)  # 每个 index 的历史修改记录

    def set(self, index: int, val: int) -> None:
        self.history[index].append((self.cur_snap_id, val))

    def snap(self) -> int:
        self.cur_snap_id += 1
        return self.cur_snap_id - 1

    def get(self, index: int, snap_id: int) -> int:
        # 找快照编号 <= snap_id 的最后一次修改记录
        # 等价于找快照编号 >= snap_id+1 的第一个修改记录，它的上一个就是答案
        j = bisect_left(self.history[index], (snap_id + 1,)) - 1
        return self.history[index][j][1] if j >= 0 else 0

# 20240804
# https://leetcode.cn/problems/subtree-of-another-tree/description/?envType=daily-question&envId=2024-08-04
class Solution(object):
    def isSubtree(self, s, t):
        """
        :type s: TreeNode
        :type t: TreeNode
        :rtype: bool
        """
        if not s and not t:
            return True
        if not s or not t:
            return False
        return self.isSameTree(s, t) or self.isSubtree(s.left, t) or self.isSubtree(s.right, t)
        
    def isSameTree(self, s, t):
        if not s and not t:
            return True
        if not s or not t:
            return False
        return s.val == t.val and self.isSameTree(s.left, t.left) and self.isSameTree(s.right, t.right)

# 20240812
# https://leetcode.cn/problems/implement-magic-dictionary/description/?envType=daily-question&envId=2024-08-12
class Trie:
    __slots__ = "children", "is_end"

    def __init__(self):
        self.children: List[Optional[Trie]] = [None] * 26
        self.is_end = False

    def insert(self, w: str) -> None:
        node = self
        for c in w:
            idx = ord(c) - ord("a")
            if node.children[idx] is None:
                node.children[idx] = Trie()
            node = node.children[idx]
        node.is_end = True

    def search(self, w: str) -> bool:
        def dfs(i: int, node: Optional[Trie], diff: int) -> bool:
            if i == len(w):
                return diff == 1 and node.is_end
            j = ord(w[i]) - ord("a")
            if node.children[j] and dfs(i + 1, node.children[j], diff):
                return True
            return diff == 0 and any(
                node.children[k] and dfs(i + 1, node.children[k], 1)
                for k in range(26)
                if k != j
            )

        return dfs(0, self, 0)

class MagicDictionary:
    def __init__(self):
        self.trie = Trie()

    def buildDict(self, dictionary: List[str]) -> None:
        for w in dictionary:
            self.trie.insert(w)

    def search(self, searchWord: str) -> bool:
        return self.trie.search(searchWord)

class BookMyShow:
    def __init__(self, n: int, m: int):
        self.n = n
        self.m = m
        self.min = [0] * (2 << n.bit_length())  # 相比 4n 空间更小
        self.sum = [0] * (2 << n.bit_length())

    # 线段树：把下标 i 上的元素值增加 val
    def update(self, o: int, l: int, r: int, i: int, val: int) -> None:
        if l == r:
            self.min[o] += val
            self.sum[o] += val
            return
        m = (l + r) // 2
        if i <= m:
            self.update(o * 2, l, m, i, val)
        else:
            self.update(o * 2 + 1, m + 1, r, i, val)
        self.min[o] = min(self.min[o * 2], self.min[o * 2 + 1])
        self.sum[o] = self.sum[o * 2] + self.sum[o * 2 + 1]

    # 线段树：返回区间 [L,R] 内的元素和
    def query_sum(self, o: int, l: int, r: int, L: int, R: int) -> int:
        if L <= l and r <= R:
            return self.sum[o]
        res = 0
        m = (l + r) // 2
        if L <= m:
            res = self.query_sum(o * 2, l, m, L, R)
        if R > m:
            res += self.query_sum(o * 2 + 1, m + 1, r, L, R)
        return res

    # 线段树：返回区间 [0,R] 中 <= val 的最靠左的位置，不存在时返回 -1
    def find_first(self, o: int, l: int, r: int, R: int, val: int) -> int:
        if self.min[o] > val:
            return -1  # 整个区间的元素值都大于 val
        if l == r:
            return l
        m = (l + r) // 2
        if self.min[o * 2] <= val:
            return self.find_first(o * 2, l, m, R, val)
        if R > m:
            return self.find_first(o * 2 + 1, m + 1, r, R, val)
        return -1

    def gather(self, k: int, maxRow: int) -> List[int]:
        # 找第一个能倒入 k 升水的水桶
        r = self.find_first(1, 0, self.n - 1, maxRow, self.m - k)
        if r < 0:  # 没有这样的水桶
            return []
        c = self.query_sum(1, 0, self.n - 1, r, r)
        self.update(1, 0, self.n - 1, r, k)  # 倒水
        return [r, c]

    def scatter(self, k: int, maxRow: int) -> bool:
        # [0,maxRow] 的接水量之和
        s = self.query_sum(1, 0, self.n - 1, 0, maxRow)
        if s > self.m * (maxRow + 1) - k:
            return False  # 水桶已经装了太多的水
        # 从第一个没有装满的水桶开始
        i = self.find_first(1, 0, self.n - 1, maxRow, self.m - 1)
        while k:
            left = min(self.m - self.query_sum(1, 0, self.n - 1, i, i), k)
            self.update(1, 0, self.n - 1, i, left)  # 倒水
            k -= left
            i += 1
        return True

# 20240930
# https://leetcode.cn/problems/seat-reservation-manager/description/?envType=daily-question&envId=2024-09-30
class SeatManager:
    def __init__(self, _: int):
        self.seats = 0  # 一开始没有椅子
        self.available = []

    def reserve(self) -> int:
        if self.available:  # 有空出来的椅子
            return heappop(self.available)  # 坐编号最小的
        self.seats += 1  # 添加一把新的椅子
        return self.seats

    def unreserve(self, seatNumber: int) -> None:
        heappush(self.available, seatNumber)  # 有人离开了椅子

# 20241109
# https://leetcode.cn/problems/design-neighbor-sum-service/description/?envType=daily-question&envId=2024-11-09
class NeighborSum:

    def __init__(self, grid: List[List[int]]):
        self.grid = grid
        self.d = {}
        self.dirs = ((-1, 0, 1, 0, -1), (-1, 1, 1, -1, -1))
        for i, row in enumerate(grid):
            for j, x in enumerate(row):
                self.d[x] = (i, j)

    def adjacentSum(self, value: int) -> int:
        return self.cal(value, 0)

    def cal(self, value: int, k: int):
        i, j = self.d[value]
        s = 0
        for a, b in pairwise(self.dirs[k]):
            x, y = i + a, j + b
            if 0 <= x < len(self.grid) and 0 <= y < len(self.grid[0]):
                s += self.grid[x][y]
        return s

    def diagonalSum(self, value: int) -> int:
        return self.cal(value, 1)

# 20241223
# https://leetcode.cn/problems/exam-room/description/?envType=daily-question&envId=2024-12-23
class ExamRoom:

    def __init__(self, n: int):
        from sortedcontainers import SortedList
        def func(x):
            res = x[1]-x[0]-1 if x[0] == -1 or x[1] == n else (x[1]-x[0])//2
            return res
        self.seated = SortedList(key = lambda x: (func(x), -x[0]))
        self.seated.add((-1, n))
        self.n = n
        self.left, self.right = {}, {}


    def seat(self) -> int:
        n = self.n
        a, b = self.seated.pop()
        if a == -1:
            p = 0
        elif b == n:
            p = n-1
        else:
            p = (a+b)//2
        
        self.left[p] = a
        self.left[b] = p
        self.right[a] = p
        self.right[p] = b
        
        self.seated.add((a, p))
        self.seated.add((p, b))
        return p

    def leave(self, p: int) -> None:
        a, b = self.left[p], self.right[p]
        self.left[b] = a
        self.right[a] = b
        self.seated.remove((a, p))
        self.seated.remove((p, b))
        self.seated.add((a, b))

# 20250102
# https://leetcode.cn/problems/my-calendar-i/description/?envType=daily-question&envId=2025-01-02
class MyCalendar:

    def __init__(self):
        self.cal = [[-1, -1], [inf, inf]]    
    
    def book(self, startTime: int, endTime: int) -> bool:
        i = bisect_right(self.cal, [startTime, endTime])
        if startTime < self.cal[i - 1][1] or endTime > self.cal[i][0]: return False
        self.cal.insert(i, [startTime, endTime])
        return True

# 20250103
# https://leetcode.cn/problems/my-calendar-ii/description/?envType=daily-question&envId=2025-01-03
class MyCalendarTwo:
    def __init__(self):
        self.bookings = []
        self.overlaps = []

    def book(self, startTime: int, endTime: int) -> bool:
        # 检查是否会导致三重预订
        for overlap_start, overlap_end in self.overlaps:
            if max(startTime, overlap_start) < min(endTime, overlap_end):
                return False
        
        # 更新 overlaps 列表
        for booked_start, booked_end in self.bookings:
            if max(startTime, booked_start) < min(endTime, booked_end):
                self.overlaps.append((max(startTime, booked_start), min(endTime, booked_end)))
        
        # 添加到 bookings 列表
        self.bookings.append((startTime, endTime))
        return True

# 20250104
# https://leetcode.cn/problems/my-calendar-iii/description/?envType=daily-question&envId=2025-01-04
class MyCalendarThree:

    def __init__(self):
        self.cal, self.cnt = [0, inf], [0, 0]

    def book(self, startTime: int, endTime: int) -> int:
        l, r = bisect_right(self.cal, startTime), bisect_left(self.cal, endTime)
        if endTime < self.cal[r]: 
            self.cal.insert(r, endTime)
            self.cnt.insert(r, self.cnt[r - 1])
        for i in range(l, r): self.cnt[i] += 1
        if startTime == self.cal[l - 1]: 
            self.cnt[l - 1] += 1
        else:
            self.cal.insert(l, startTime)
            self.cnt.insert(l, self.cnt[l - 1] + 1)
        return max(self.cnt)

# 20250105
# https://leetcode.cn/problems/design-an-atm-machine/description/?envType=daily-question&envId=2025-01-05
class ATM:
    def __init__(self):
        self.d = [20, 50, 100, 200, 500]
        self.m = len(self.d)
        self.cnt = [0] * self.m

    def deposit(self, banknotesCount: List[int]) -> None:
        for i, x in enumerate(banknotesCount):
            self.cnt[i] += x

    def withdraw(self, amount: int) -> List[int]:
        ans = [0] * self.m
        for i in reversed(range(self.m)):
            ans[i] = min(amount // self.d[i], self.cnt[i])
            amount -= ans[i] * self.d[i]
        if amount > 0:
            return [-1]
        for i, x in enumerate(ans):
            self.cnt[i] -= x
        return ans

if __name__ == "__main__":
    result = Solution()
    print(result)